📄 rt_netlink.c
字号:
/* Kernel routing table updates using netlink over GNU/Linux system. * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro * * This file is part of GNU Zebra. * * GNU Zebra is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */#include <zebra.h>/* Hack for GNU libc version 2. */#ifndef MSG_TRUNC#define MSG_TRUNC 0x20#endif /* MSG_TRUNC */#include "linklist.h"#include "if.h"#include "log.h"#include "prefix.h"#include "connected.h"#include "table.h"#include "rib.h"#include "zebra/zserv.h"#include "zebra/redistribute.h"#include "zebra/interface.h"#include "zebra/debug.h"/* Socket interface to kernel */struct nlsock{ int sock; int seq; struct sockaddr_nl snl; char *name;} netlink = { -1, 0, {0}, "netlink-listen" }, /* kernel messages */ netlink_cmd = { -1, 0, {0}, "netlink-cmd" }, /* command channel */ netlink_addr = {-1, 0, {0}, "netlink-addr" }; /* address channel */struct message nlmsg_str[] ={ {RTM_NEWROUTE, "RTM_NEWROUTE"}, {RTM_DELROUTE, "RTM_DELROUTE"}, {RTM_GETROUTE, "RTM_GETROUTE"}, {RTM_NEWLINK, "RTM_NEWLINK"}, {RTM_DELLINK, "RTM_DELLINK"}, {RTM_GETLINK, "RTM_GETLINK"}, {RTM_NEWADDR, "RTM_NEWADDR"}, {RTM_DELADDR, "RTM_DELADDR"}, {RTM_GETADDR, "RTM_GETADDR"}, {0, NULL}};extern int rtm_table_default;/* Make socket for Linux netlink interface. */static intnetlink_socket (struct nlsock *nl, unsigned long groups){ int ret; struct sockaddr_nl snl; int sock; int namelen; sock = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock < 0) { zlog (NULL, LOG_ERR, "Can't open %s socket: %s", nl->name, strerror (errno)); return -1; } ret = fcntl (sock, F_SETFL, O_NONBLOCK); if (ret < 0) { zlog (NULL, LOG_ERR, "Can't set %s socket flags: %s", nl->name, strerror (errno)); close (sock); return -1; } memset (&snl, 0, sizeof snl); snl.nl_family = AF_NETLINK; snl.nl_groups = groups; /* Bind the socket to the netlink structure for anything. */ ret = bind (sock, (struct sockaddr *) &snl, sizeof snl); if (ret < 0) { zlog (NULL, LOG_ERR, "Can't bind %s socket to group 0x%x: %s", nl->name, snl.nl_groups, strerror (errno)); close (sock); return -1; } /* multiple netlink sockets will have different nl_pid */ namelen = sizeof snl; ret = getsockname (sock, (struct sockaddr *) &snl, &namelen); if (ret < 0 || namelen != sizeof snl) { zlog (NULL, LOG_ERR, "Can't get %s socket name: %s", nl->name, strerror (errno)); close (sock); return -1; } nl->snl = snl; nl->sock = sock; return ret;}/* Get type specified information from netlink. */static intnetlink_request (int family, int type, struct nlsock *nl){ int ret; struct sockaddr_nl snl; struct { struct nlmsghdr nlh; struct rtgenmsg g; } req; /* Check netlink socket. */ if (nl->sock < 0) { zlog (NULL, LOG_ERR, "%s socket isn't active.", nl->name); return -1; } memset (&snl, 0, sizeof snl); snl.nl_family = AF_NETLINK; req.nlh.nlmsg_len = sizeof req; req.nlh.nlmsg_type = type; req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; req.nlh.nlmsg_pid = 0; req.nlh.nlmsg_seq = ++nl->seq; req.g.rtgen_family = family; ret = sendto (nl->sock, (void*) &req, sizeof req, 0, (struct sockaddr*) &snl, sizeof snl); if (ret < 0) { zlog (NULL, LOG_ERR, "%s sendto failed: %s", nl->name, strerror (errno)); return -1; } return 0;}/* Receive message from netlink interface and pass those information to the given function. */static intnetlink_parse_info (int (*filter) (struct sockaddr_nl *, struct nlmsghdr *), struct nlsock *nl){ int status; int ret = 0; int error; while (1) { char buf[4096]; struct iovec iov = { buf, sizeof buf }; struct sockaddr_nl snl; struct msghdr msg = { (void*)&snl, sizeof snl, &iov, 1, NULL, 0, 0}; struct nlmsghdr *h; status = recvmsg (nl->sock, &msg, 0); if (status < 0) { if (errno == EINTR) continue; if (errno == EWOULDBLOCK || errno == EAGAIN) break; zlog (NULL, LOG_ERR, "%s recvmsg overrun", nl->name); continue; } if (snl.nl_pid != 0) { zlog (NULL, LOG_ERR, "Ignoring non kernel message from pid %u", snl.nl_pid); continue; } if (status == 0) { zlog (NULL, LOG_ERR, "%s EOF", nl->name); return -1; } if (msg.msg_namelen != sizeof snl) { zlog (NULL, LOG_ERR, "%s sender address length error: length %d", nl->name, msg.msg_namelen); return -1; } for (h = (struct nlmsghdr *) buf; NLMSG_OK (h, status); h = NLMSG_NEXT (h, status)) { /* Finish of reading. */ if (h->nlmsg_type == NLMSG_DONE) return ret; /* Error handling. */ if (h->nlmsg_type == NLMSG_ERROR) { struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA (h); /* If the error field is zero, then this is an ACK */ if (err->error == 0) { if (IS_ZEBRA_DEBUG_KERNEL) { zlog_info("%s: %s ACK: type=%s(%u), seq=%u, pid=%d", __FUNCTION__, nl->name, lookup (nlmsg_str, err->msg.nlmsg_type), err->msg.nlmsg_type, err->msg.nlmsg_seq, err->msg.nlmsg_pid); } /* return if not a multipart message, otherwise continue */ if(!(h->nlmsg_flags & NLM_F_MULTI)) { return 0; } continue; } if (h->nlmsg_len < NLMSG_LENGTH (sizeof (struct nlmsgerr))) { zlog (NULL, LOG_ERR, "%s error: message truncated", nl->name); return -1; } zlog (NULL, LOG_ERR, "%s error: %s, type=%s(%u), seq=%u, pid=%d", nl->name, strerror (-err->error), lookup (nlmsg_str, err->msg.nlmsg_type), err->msg.nlmsg_type, err->msg.nlmsg_seq, err->msg.nlmsg_pid); /* ret = -1; continue; */ return -1; } /* OK we got netlink message. */ if (IS_ZEBRA_DEBUG_KERNEL) zlog_info ("netlink_parse_info: %s type %s(%u), seq=%u, pid=%d", nl->name, lookup (nlmsg_str, h->nlmsg_type), h->nlmsg_type, h->nlmsg_seq, h->nlmsg_pid); /* skip unsolicited messages originating from command socket */ if (nl != &netlink_cmd && h->nlmsg_pid == netlink_cmd.snl.nl_pid) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_info ("netlink_parse_info: %s packet comes from %s", nl->name, netlink_cmd.name); continue; } error = (*filter) (&snl, h); if (error < 0) { zlog (NULL, LOG_ERR, "%s filter function error", nl->name); ret = error; } } /* After error care. */ if (msg.msg_flags & MSG_TRUNC) { zlog (NULL, LOG_ERR, "%s error: message truncated", nl->name); continue; } if (status) { zlog (NULL, LOG_ERR, "%s error: data remnant size %d", nl->name, status); return -1; } } return ret;}/* Utility function for parse rtattr. */static voidnetlink_parse_rtattr (struct rtattr **tb, int max, struct rtattr *rta, int len){ while (RTA_OK(rta, len)) { if (rta->rta_type <= max) tb[rta->rta_type] = rta; rta = RTA_NEXT(rta,len); }}/* Called from interface_lookup_netlink(). This function is only used during bootstrap. */intnetlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h){ int len; struct ifinfomsg *ifi; struct rtattr *tb[IFLA_MAX + 1]; struct interface *ifp; char *name; int i; ifi = NLMSG_DATA (h); if (h->nlmsg_type != RTM_NEWLINK) return 0; len = h->nlmsg_len - NLMSG_LENGTH (sizeof (struct ifinfomsg)); if (len < 0) return -1; /* Looking up interface name. */ memset (tb, 0, sizeof tb); netlink_parse_rtattr (tb, IFLA_MAX, IFLA_RTA (ifi), len); if (tb[IFLA_IFNAME] == NULL) return -1; name = (char *)RTA_DATA(tb[IFLA_IFNAME]); /* Add interface. */ ifp = if_get_by_name (name); ifp->ifindex = ifi->ifi_index; ifp->flags = ifi->ifi_flags & 0x0000fffff; ifp->mtu = *(int *)RTA_DATA (tb[IFLA_MTU]); ifp->metric = 1; /* Hardware type and address. */ ifp->hw_type = ifi->ifi_type; if (tb[IFLA_ADDRESS]) { int hw_addr_len; hw_addr_len = RTA_PAYLOAD(tb[IFLA_ADDRESS]); if (hw_addr_len > INTERFACE_HWADDR_MAX) zlog_warn ("Hardware address is too large: %d", hw_addr_len); else { ifp->hw_addr_len = hw_addr_len; memcpy (ifp->hw_addr, RTA_DATA(tb[IFLA_ADDRESS]), hw_addr_len); for (i = 0; i < hw_addr_len; i++) if (ifp->hw_addr[i] != 0) break; if (i == hw_addr_len) ifp->hw_addr_len = 0; else ifp->hw_addr_len = hw_addr_len; } } if_add_update (ifp); return 0;}/* Lookup interface IPv4/IPv6 address. */intnetlink_interface_addr (struct sockaddr_nl *snl, struct nlmsghdr *h){ int len; struct ifaddrmsg *ifa; struct rtattr *tb [IFA_MAX + 1]; struct interface *ifp; void *addr = NULL; void *broad = NULL; u_char flags = 0; char *label = NULL; ifa = NLMSG_DATA (h); if (ifa->ifa_family != AF_INET #ifdef HAVE_IPV6 && ifa->ifa_family != AF_INET6#endif /* HAVE_IPV6 */ ) return 0; if (h->nlmsg_type != RTM_NEWADDR && h->nlmsg_type != RTM_DELADDR) return 0; len = h->nlmsg_len - NLMSG_LENGTH(sizeof (struct ifaddrmsg)); if (len < 0) return -1; memset (tb, 0, sizeof tb); netlink_parse_rtattr (tb, IFA_MAX, IFA_RTA (ifa), len); ifp = if_lookup_by_index (ifa->ifa_index); if (ifp == NULL) { zlog_err ("netlink_interface_addr can't find interface by index %d", ifa->ifa_index); return -1; } if (tb[IFA_ADDRESS] == NULL) tb[IFA_ADDRESS] = tb[IFA_LOCAL]; if (ifp->flags & IFF_POINTOPOINT) { if (tb[IFA_LOCAL]) { addr = RTA_DATA (tb[IFA_LOCAL]); if (tb[IFA_ADDRESS]) broad = RTA_DATA (tb[IFA_ADDRESS]); else broad = NULL; } else { if (tb[IFA_ADDRESS]) addr = RTA_DATA (tb[IFA_ADDRESS]); else addr = NULL; } } else { if (tb[IFA_ADDRESS]) addr = RTA_DATA (tb[IFA_ADDRESS]); else addr = NULL; if (tb[IFA_BROADCAST]) broad = RTA_DATA(tb[IFA_BROADCAST]); else broad = NULL; } /* Flags. */ if (ifa->ifa_flags & IFA_F_SECONDARY) SET_FLAG (flags, ZEBRA_IFA_SECONDARY); /* Label */ if (tb[IFA_LABEL]) label = (char *) RTA_DATA (tb[IFA_LABEL]); if (ifp && label && strcmp (ifp->name, label) == 0) label = NULL; /* Register interface address to the interface. */ if (ifa->ifa_family == AF_INET) { if (h->nlmsg_type == RTM_NEWADDR) connected_add_ipv4 (ifp, flags, (struct in_addr *) addr, ifa->ifa_prefixlen, (struct in_addr *) broad, label); else connected_delete_ipv4 (ifp, flags, (struct in_addr *) addr, ifa->ifa_prefixlen, (struct in_addr *) broad, label); }#ifdef HAVE_IPV6 if (ifa->ifa_family == AF_INET6) { if (h->nlmsg_type == RTM_NEWADDR) connected_add_ipv6 (ifp, (struct in6_addr *) addr, ifa->ifa_prefixlen, (struct in6_addr *) broad); else connected_delete_ipv6 (ifp, (struct in6_addr *) addr, ifa->ifa_prefixlen, (struct in6_addr *) broad); }#endif /* HAVE_IPV6*/ return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -