} netlink = { -1, 0, {0}, "netlink-listen"}, /* kernel messages */
netlink_cmd = { -1, 0, {0}, "netlink-cmd"}; /* command channel */
-struct message nlmsg_str[] = {
+static const struct message nlmsg_str[] = {
{RTM_NEWROUTE, "RTM_NEWROUTE"},
{RTM_DELROUTE, "RTM_DELROUTE"},
{RTM_GETROUTE, "RTM_GETROUTE"},
{0, NULL}
};
-const char *nexthop_types_desc[] =
+static const char *nexthop_types_desc[] =
{
"none",
"Directly connected",
"Null0 nexthop",
};
-
extern struct zebra_t zebrad;
extern struct zebra_privs_t zserv_privs;
ifp->ifindex = ifi_index;
}
-/* Make socket for Linux netlink interface. */
static int
-netlink_socket (struct nlsock *nl, unsigned long groups)
+netlink_recvbuf (struct nlsock *nl, uint32_t newsize)
{
+ u_int32_t oldsize;
+ socklen_t newlen = sizeof(newsize);
+ socklen_t oldlen = sizeof(oldsize);
int ret;
- struct sockaddr_nl snl;
- int sock;
- int namelen;
- int save_errno;
- sock = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
- if (sock < 0)
+ ret = getsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &oldsize, &oldlen);
+ if (ret < 0)
{
- zlog (NULL, LOG_ERR, "Can't open %s socket: %s", nl->name,
- safe_strerror (errno));
+ zlog (NULL, LOG_ERR, "Can't get %s receive buffer size: %s", nl->name,
+ safe_strerror (errno));
return -1;
}
- ret = fcntl (sock, F_SETFL, O_NONBLOCK);
+ ret = setsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &nl_rcvbufsize,
+ sizeof(nl_rcvbufsize));
if (ret < 0)
{
- zlog (NULL, LOG_ERR, "Can't set %s socket flags: %s", nl->name,
- safe_strerror (errno));
- close (sock);
+ zlog (NULL, LOG_ERR, "Can't set %s receive buffer size: %s", nl->name,
+ safe_strerror (errno));
return -1;
}
- /* Set receive buffer size if it's set from command line */
- if (nl_rcvbufsize)
+ ret = getsockopt(nl->sock, SOL_SOCKET, SO_RCVBUF, &newsize, &newlen);
+ if (ret < 0)
{
- u_int32_t oldsize, oldlen;
- u_int32_t newsize, newlen;
+ zlog (NULL, LOG_ERR, "Can't get %s receive buffer size: %s", nl->name,
+ safe_strerror (errno));
+ return -1;
+ }
- oldlen = sizeof(oldsize);
- newlen = sizeof(newsize);
+ zlog (NULL, LOG_INFO,
+ "Setting netlink socket receive buffer size: %u -> %u",
+ oldsize, newsize);
+ return 0;
+}
- ret = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &oldsize, &oldlen);
- if (ret < 0)
- {
- zlog (NULL, LOG_ERR, "Can't get %s receive buffer size: %s", nl->name,
- safe_strerror (errno));
- close (sock);
- return -1;
- }
-
- ret = setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &nl_rcvbufsize,
- sizeof(nl_rcvbufsize));
- if (ret < 0)
- {
- zlog (NULL, LOG_ERR, "Can't set %s receive buffer size: %s", nl->name,
- safe_strerror (errno));
- close (sock);
- return -1;
- }
-
- ret = getsockopt(sock, SOL_SOCKET, SO_RCVBUF, &newsize, &newlen);
- if (ret < 0)
- {
- zlog (NULL, LOG_ERR, "Can't get %s receive buffer size: %s", nl->name,
- safe_strerror (errno));
- close (sock);
- return -1;
- }
+/* Make socket for Linux netlink interface. */
+static int
+netlink_socket (struct nlsock *nl, unsigned long groups)
+{
+ int ret;
+ struct sockaddr_nl snl;
+ int sock;
+ int namelen;
+ int save_errno;
- zlog (NULL, LOG_INFO,
- "Setting netlink socket receive buffer size: %u -> %u",
- oldsize, newsize);
+ sock = socket (AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (sock < 0)
+ {
+ zlog (NULL, LOG_ERR, "Can't open %s socket: %s", nl->name,
+ safe_strerror (errno));
+ return -1;
}
memset (&snl, 0, sizeof snl);
return ret;
}
-int
-set_netlink_blocking (struct nlsock *nl, int *flags)
-{
-
- /* Change socket flags for blocking I/O. */
- if ((*flags = fcntl (nl->sock, F_GETFL, 0)) < 0)
- {
- zlog (NULL, LOG_ERR, "%s:%i F_GETFL error: %s",
- __FUNCTION__, __LINE__, safe_strerror (errno));
- return -1;
- }
- *flags &= ~O_NONBLOCK;
- if (fcntl (nl->sock, F_SETFL, *flags) < 0)
- {
- zlog (NULL, LOG_ERR, "%s:%i F_SETFL error: %s",
- __FUNCTION__, __LINE__, safe_strerror (errno));
- return -1;
- }
- return 0;
-}
-
-int
-set_netlink_nonblocking (struct nlsock *nl, int *flags)
-{
- /* Restore socket flags for nonblocking I/O */
- *flags |= O_NONBLOCK;
- if (fcntl (nl->sock, F_SETFL, *flags) < 0)
- {
- zlog (NULL, LOG_ERR, "%s:%i F_SETFL error: %s",
- __FUNCTION__, __LINE__, safe_strerror (errno));
- return -1;
- }
- return 0;
-}
-
/* Get type specified information from netlink. */
static int
netlink_request (int family, int type, struct nlsock *nl)
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_pid = nl->snl.nl_pid;
req.nlh.nlmsg_seq = ++nl->seq;
req.g.rtgen_family = family;
struct sockaddr_nl snl;
struct msghdr msg = { (void *) &snl, sizeof snl, &iov, 1, NULL, 0, 0 };
struct nlmsghdr *h;
- int save_errno;
-
- if (zserv_privs.change (ZPRIVS_RAISE))
- zlog (NULL, LOG_ERR, "Can't raise privileges");
status = recvmsg (nl->sock, &msg, 0);
- save_errno = errno;
-
- if (zserv_privs.change (ZPRIVS_LOWER))
- zlog (NULL, LOG_ERR, "Can't lower privileges");
-
if (status < 0)
{
- if (save_errno == EINTR)
+ if (errno == EINTR)
continue;
- if (save_errno == EWOULDBLOCK || save_errno == EAGAIN)
+ if (errno == EWOULDBLOCK || errno == EAGAIN)
break;
zlog (NULL, LOG_ERR, "%s recvmsg overrun: %s",
- nl->name, safe_strerror(save_errno));
+ nl->name, safe_strerror(errno));
continue;
}
return -1;
}
- /* JF: Ignore messages that aren't from the kernel */
- if ( snl.nl_pid != 0 )
- {
- zlog ( NULL, LOG_ERR, "Ignoring message from pid %u", snl.nl_pid );
- continue;
- }
-
for (h = (struct nlmsghdr *) buf; NLMSG_OK (h, (unsigned int) status);
h = NLMSG_NEXT (h, status))
{
if (h->nlmsg_type == NLMSG_ERROR)
{
struct nlmsgerr *err = (struct nlmsgerr *) NLMSG_DATA (h);
+ int errnum = err->error;
+ int msg_type = err->msg.nlmsg_type;
/* If the error field is zero, then this is an ACK */
if (err->error == 0)
return -1;
}
- /* Deal with Error Noise - MAG */
- {
- int loglvl = LOG_ERR;
- int errnum = err->error;
- int msg_type = err->msg.nlmsg_type;
-
- if (nl == &netlink_cmd
- && (-errnum == ENODEV || -errnum == ESRCH)
- && (msg_type == RTM_NEWROUTE || msg_type == RTM_DELROUTE))
- loglvl = LOG_DEBUG;
-
- zlog (NULL, loglvl, "%s error: %s, type=%s(%u), "
- "seq=%u, pid=%u",
- nl->name, safe_strerror (-errnum),
- lookup (nlmsg_str, msg_type),
- msg_type, err->msg.nlmsg_seq, err->msg.nlmsg_pid);
- }
- /*
- ret = -1;
- continue;
- */
+ /* Deal with errors that occur because of races in link handling */
+ if (nl == &netlink_cmd
+ && ((msg_type == RTM_DELROUTE &&
+ (-errnum == ENODEV || -errnum == ESRCH))
+ || (msg_type == RTM_NEWROUTE && -errnum == EEXIST)))
+ {
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug ("%s: error: %s type=%s(%u), seq=%u, pid=%u",
+ nl->name, safe_strerror (-errnum),
+ lookup (nlmsg_str, msg_type),
+ msg_type, err->msg.nlmsg_seq, err->msg.nlmsg_pid);
+ return 0;
+ }
+
+ zlog_err ("%s error: %s, type=%s(%u), seq=%u, pid=%u",
+ nl->name, safe_strerror (-errnum),
+ lookup (nlmsg_str, msg_type),
+ msg_type, err->msg.nlmsg_seq, err->msg.nlmsg_pid);
return -1;
}
/* Called from interface_lookup_netlink(). This function is only used
during bootstrap. */
-int
+static int
netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h)
{
int len;
ifp = if_get_by_name (name);
set_ifindex(ifp, ifi->ifi_index);
ifp->flags = ifi->ifi_flags & 0x0000fffff;
- ifp->mtu6 = ifp->mtu = *(int *) RTA_DATA (tb[IFLA_MTU]);
+ ifp->mtu6 = ifp->mtu = *(uint32_t *) RTA_DATA (tb[IFLA_MTU]);
ifp->metric = 1;
/* Hardware type and address. */
}
/* Lookup interface IPv4/IPv6 address. */
-int
+static int
netlink_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;
+ void *addr;
+ void *broad;
u_char flags = 0;
char *label = NULL;
}
}
+ /* logic copied from iproute2/ip/ipaddress.c:print_addrinfo() */
+ if (tb[IFA_LOCAL] == NULL)
+ tb[IFA_LOCAL] = tb[IFA_ADDRESS];
if (tb[IFA_ADDRESS] == NULL)
tb[IFA_ADDRESS] = tb[IFA_LOCAL];
- if (ifp->flags & IFF_POINTOPOINT)
+ /* local interface address */
+ addr = (tb[IFA_LOCAL] ? RTA_DATA(tb[IFA_LOCAL]) : NULL);
+
+ /* is there a peer address? */
+ if (tb[IFA_ADDRESS] &&
+ memcmp(RTA_DATA(tb[IFA_ADDRESS]), RTA_DATA(tb[IFA_LOCAL]), RTA_PAYLOAD(tb[IFA_ADDRESS])))
{
- if (tb[IFA_LOCAL])
- {
- addr = RTA_DATA (tb[IFA_LOCAL]);
- if (tb[IFA_ADDRESS] &&
- memcmp(RTA_DATA(tb[IFA_ADDRESS]),RTA_DATA(tb[IFA_LOCAL]),4))
- /* if IFA_ADDRESS != IFA_LOCAL, then it's the peer address */
- broad = RTA_DATA (tb[IFA_ADDRESS]);
- else
- broad = NULL;
- }
- else
- {
- if (tb[IFA_ADDRESS])
- addr = RTA_DATA (tb[IFA_ADDRESS]);
- else
- addr = NULL;
- }
+ broad = RTA_DATA(tb[IFA_ADDRESS]);
+ SET_FLAG (flags, ZEBRA_IFA_PEER);
}
else
+ /* seeking a broadcast address */
+ broad = (tb[IFA_BROADCAST] ? RTA_DATA(tb[IFA_BROADCAST]) : NULL);
+
+ /* addr is primary key, SOL if we don't have one */
+ if (addr == NULL)
{
- 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;
+ zlog_debug ("%s: NULL address", __func__);
+ return -1;
}
/* Flags. */
if (ifa->ifa_family == AF_INET6)
{
if (h->nlmsg_type == RTM_NEWADDR)
- connected_add_ipv6 (ifp,
+ connected_add_ipv6 (ifp, flags,
(struct in6_addr *) addr, ifa->ifa_prefixlen,
(struct in6_addr *) broad, label);
else
}
/* Looking up routing table by netlink interface. */
-int
+static int
netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h)
{
int len;
void *dest;
void *gate;
+ void *src;
rtm = NLMSG_DATA (h);
metric = 0;
dest = NULL;
gate = NULL;
+ src = NULL;
if (tb[RTA_OIF])
index = *(int *) RTA_DATA (tb[RTA_OIF]);
else
dest = anyaddr;
+ if (tb[RTA_PREFSRC])
+ src = RTA_DATA (tb[RTA_PREFSRC]);
+
/* Multipath treatment is needed. */
if (tb[RTA_GATEWAY])
gate = RTA_DATA (tb[RTA_GATEWAY]);
memcpy (&p.prefix, dest, 4);
p.prefixlen = rtm->rtm_dst_len;
- rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, index, table, metric, 0);
+ rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, src, index, table, metric, 0);
}
#ifdef HAVE_IPV6
if (rtm->rtm_family == AF_INET6)
return 0;
}
-struct message rtproto_str[] = {
+static const struct message rtproto_str[] = {
{RTPROT_REDIRECT, "redirect"},
{RTPROT_KERNEL, "kernel"},
{RTPROT_BOOT, "boot"},
};
/* Routing information change from the kernel. */
-int
+static int
netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
{
int len;
int index;
int table;
+ int metric;
+
void *dest;
void *gate;
+ void *src;
rtm = NLMSG_DATA (h);
}
index = 0;
+ metric = 0;
dest = NULL;
gate = NULL;
+ src = NULL;
if (tb[RTA_OIF])
index = *(int *) RTA_DATA (tb[RTA_OIF]);
if (tb[RTA_GATEWAY])
gate = RTA_DATA (tb[RTA_GATEWAY]);
+ if (tb[RTA_PREFSRC])
+ src = RTA_DATA (tb[RTA_PREFSRC]);
+
+ if (h->nlmsg_type == RTM_NEWROUTE && tb[RTA_PRIORITY])
+ metric = *(int *) RTA_DATA(tb[RTA_PRIORITY]);
+
if (rtm->rtm_family == AF_INET)
{
struct prefix_ipv4 p;
}
if (h->nlmsg_type == RTM_NEWROUTE)
- rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, table, 0, 0);
+ rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, src, index, table, metric, 0);
else
rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, table);
}
}
if (h->nlmsg_type == RTM_NEWROUTE)
- rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, 0, 0, 0);
+ rib_add_ipv6 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, table, metric, 0);
else
- rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, 0);
+ rib_delete_ipv6 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, table);
}
#endif /* HAVE_IPV6 */
return 0;
}
-int
+static int
netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
{
int len;
return 0;
}
-int
+static int
netlink_information_fetch (struct sockaddr_nl *snl, struct nlmsghdr *h)
{
+ /* JF: Ignore messages that aren't from the kernel */
+ if ( snl->nl_pid != 0 )
+ {
+ zlog ( NULL, LOG_ERR, "Ignoring message from pid %u", snl->nl_pid );
+ return 0;
+ }
+
switch (h->nlmsg_type)
{
case RTM_NEWROUTE:
interface_lookup_netlink (void)
{
int ret;
- int flags;
- int snb_ret;
-
- /*
- * Change netlink socket flags to blocking to ensure we get
- * a reply via nelink_parse_info
- */
- snb_ret = set_netlink_blocking (&netlink_cmd, &flags);
- if (snb_ret < 0)
- zlog (NULL, LOG_WARNING,
- "%s:%i Warning: Could not set netlink socket to blocking.",
- __FUNCTION__, __LINE__);
/* Get interface information. */
ret = netlink_request (AF_PACKET, RTM_GETLINK, &netlink_cmd);
return ret;
#endif /* HAVE_IPV6 */
- /* restore socket flags */
- if (snb_ret == 0)
- set_netlink_nonblocking (&netlink_cmd, &flags);
return 0;
}
netlink_route_read (void)
{
int ret;
- int flags;
- int snb_ret;
-
- /*
- * Change netlink socket flags to blocking to ensure we get
- * a reply via nelink_parse_info
- */
- snb_ret = set_netlink_blocking (&netlink_cmd, &flags);
- if (snb_ret < 0)
- zlog (NULL, LOG_WARNING,
- "%s:%i Warning: Could not set netlink socket to blocking.",
- __FUNCTION__, __LINE__);
/* Get IPv4 routing table. */
ret = netlink_request (AF_INET, RTM_GETROUTE, &netlink_cmd);
return ret;
#endif /* HAVE_IPV6 */
- /* restore flags */
- if (snb_ret == 0)
- set_netlink_nonblocking (&netlink_cmd, &flags);
return 0;
}
/* Utility function comes from iproute2.
Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> */
-int
+static int
addattr_l (struct nlmsghdr *n, int maxlen, int type, void *data, int alen)
{
int len;
return 0;
}
-int
+static int
rta_addattr_l (struct rtattr *rta, int maxlen, int type, void *data, int alen)
{
int len;
/* Utility function comes from iproute2.
Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru> */
-int
+static int
addattr32 (struct nlmsghdr *n, int maxlen, int type, int data)
{
int len;
}
/* sendmsg() to netlink socket then recvmsg(). */
-int
+static int
netlink_talk (struct nlmsghdr *n, struct nlsock *nl)
{
int status;
struct sockaddr_nl snl;
struct iovec iov = { (void *) n, n->nlmsg_len };
struct msghdr msg = { (void *) &snl, sizeof snl, &iov, 1, NULL, 0, 0 };
- int flags = 0;
- int snb_ret;
int save_errno;
memset (&snl, 0, sizeof snl);
return -1;
}
- /*
- * Change socket flags for blocking I/O.
- * This ensures we wait for a reply in netlink_parse_info().
- */
- snb_ret = set_netlink_blocking (nl, &flags);
- if (snb_ret < 0)
- zlog (NULL, LOG_WARNING,
- "%s:%i Warning: Could not set netlink socket to blocking.",
- __FUNCTION__, __LINE__);
/*
* Get reply from netlink socket.
* The reply should either be an acknowlegement or an error.
*/
- status = netlink_parse_info (netlink_talk_filter, nl);
-
- /* Restore socket flags for nonblocking I/O */
- if (snb_ret == 0)
- set_netlink_nonblocking (nl, &flags);
-
- return status;
+ return netlink_parse_info (netlink_talk_filter, nl);
}
/* Routing table change via netlink interface. */
-int
+static int
netlink_route (int cmd, int family, void *dest, int length, void *gate,
int index, int zebra_flags, int table)
{
req.r.rtm_family = family;
req.r.rtm_table = table;
req.r.rtm_dst_len = length;
+ req.r.rtm_protocol = RTPROT_ZEBRA;
+ req.r.rtm_scope = RT_SCOPE_UNIVERSE;
if ((zebra_flags & ZEBRA_FLAG_BLACKHOLE)
|| (zebra_flags & ZEBRA_FLAG_REJECT))
if (cmd == RTM_NEWROUTE)
{
- req.r.rtm_protocol = RTPROT_ZEBRA;
- req.r.rtm_scope = RT_SCOPE_UNIVERSE;
-
if (discard)
{
if (zebra_flags & ZEBRA_FLAG_BLACKHOLE)
}
/* Routing table change via netlink interface. */
-int
+static int
netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib,
int family)
{
req.r.rtm_family = family;
req.r.rtm_table = rib->table;
req.r.rtm_dst_len = p->prefixlen;
+ req.r.rtm_protocol = RTPROT_ZEBRA;
+ req.r.rtm_scope = RT_SCOPE_UNIVERSE;
if ((rib->flags & ZEBRA_FLAG_BLACKHOLE) || (rib->flags & ZEBRA_FLAG_REJECT))
discard = 1;
if (cmd == RTM_NEWROUTE)
{
- req.r.rtm_protocol = RTPROT_ZEBRA;
- req.r.rtm_scope = RT_SCOPE_UNIVERSE;
-
if (discard)
{
if (rib->flags & ZEBRA_FLAG_BLACKHOLE)
{
addattr_l (&req.n, sizeof req, RTA_GATEWAY,
&nexthop->rgate.ipv4, bytelen);
-
+ if (nexthop->src.ipv4.s_addr)
+ addattr_l(&req.n, sizeof req, RTA_PREFSRC,
+ &nexthop->src.ipv4, bytelen);
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug("netlink_route_multipath() (recursive, "
"1 hop): nexthop via %s if %u",
{
addattr32 (&req.n, sizeof req, RTA_OIF,
nexthop->rifindex);
+ if ((nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX
+ || nexthop->rtype == NEXTHOP_TYPE_IFINDEX)
+ && nexthop->src.ipv4.s_addr)
+ addattr_l (&req.n, sizeof req, RTA_PREFSRC,
+ &nexthop->src.ipv4, bytelen);
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug("netlink_route_multipath() (recursive, "
{
addattr_l (&req.n, sizeof req, RTA_GATEWAY,
&nexthop->gate.ipv4, bytelen);
+ if (nexthop->src.ipv4.s_addr)
+ addattr_l (&req.n, sizeof req, RTA_PREFSRC,
+ &nexthop->src.ipv4, bytelen);
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug("netlink_route_multipath() (single hop): "
#endif /* HAVE_IPV6 */
if (nexthop->type == NEXTHOP_TYPE_IFINDEX
|| nexthop->type == NEXTHOP_TYPE_IFNAME
- || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX
- || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX
+ || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX)
+ {
+ addattr32 (&req.n, sizeof req, RTA_OIF, nexthop->ifindex);
+
+ if (nexthop->src.ipv4.s_addr)
+ addattr_l (&req.n, sizeof req, RTA_PREFSRC,
+ &nexthop->src.ipv4, bytelen);
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("netlink_route_multipath() (single hop): "
+ "nexthop via if %u", nexthop->ifindex);
+ }
+ else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX
|| nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME)
{
addattr32 (&req.n, sizeof req, RTA_OIF, nexthop->ifindex);
char buf[1024];
struct rtattr *rta = (void *) buf;
struct rtnexthop *rtnh;
+ union g_addr *src = NULL;
rta->rta_type = RTA_MULTIPATH;
rta->rta_len = RTA_LENGTH (0);
&nexthop->rgate.ipv4, bytelen);
rtnh->rtnh_len += sizeof (struct rtattr) + 4;
+ if (nexthop->src.ipv4.s_addr)
+ src = &nexthop->src;
+
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug("netlink_route_multipath() (recursive, "
"multihop): nexthop via %s if %u",
}
#endif /* HAVE_IPV6 */
/* ifindex */
- if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX
- || nexthop->rtype == NEXTHOP_TYPE_IFNAME
- || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX
- || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX
+ if (nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX
+ || nexthop->rtype == NEXTHOP_TYPE_IFINDEX
+ || nexthop->rtype == NEXTHOP_TYPE_IFNAME)
+ {
+ rtnh->rtnh_ifindex = nexthop->rifindex;
+ if (nexthop->src.ipv4.s_addr)
+ src = &nexthop->src;
+
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("netlink_route_multipath() (recursive, "
+ "multihop): nexthop via if %u",
+ nexthop->rifindex);
+ }
+ else if (nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX
|| nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME)
{
rtnh->rtnh_ifindex = nexthop->rifindex;
&nexthop->gate.ipv4, bytelen);
rtnh->rtnh_len += sizeof (struct rtattr) + 4;
+ if (nexthop->src.ipv4.s_addr)
+ src = &nexthop->src;
+
if (IS_ZEBRA_DEBUG_KERNEL)
zlog_debug("netlink_route_multipath() (multihop): "
"nexthop via %s if %u",
}
#endif /* HAVE_IPV6 */
/* ifindex */
- if (nexthop->type == NEXTHOP_TYPE_IFINDEX
- || nexthop->type == NEXTHOP_TYPE_IFNAME
- || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX
- || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
+ if (nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX
+ || nexthop->type == NEXTHOP_TYPE_IFINDEX
+ || nexthop->type == NEXTHOP_TYPE_IFNAME)
+ {
+ rtnh->rtnh_ifindex = nexthop->ifindex;
+ if (nexthop->src.ipv4.s_addr)
+ src = &nexthop->src;
+ if (IS_ZEBRA_DEBUG_KERNEL)
+ zlog_debug("netlink_route_multipath() (multihop): "
+ "nexthop via if %u", nexthop->ifindex);
+ }
+ else if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME
|| nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX)
{
rtnh->rtnh_ifindex = nexthop->ifindex;
SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB);
}
}
+ if (src)
+ addattr_l (&req.n, sizeof req, RTA_PREFSRC, &src->ipv4, bytelen);
if (rta->rta_len > RTA_LENGTH (0))
addattr_l (&req.n, 1024, RTA_MULTIPATH, RTA_DATA (rta),
#endif /* HAVE_IPV6 */
\f
/* Interface address modification. */
-int
+static int
netlink_address (int cmd, int family, struct interface *ifp,
struct connected *ifc)
{
if (family == AF_INET && cmd == RTM_NEWADDR)
{
- if (if_is_broadcast (ifp) && ifc->destination)
+ if (!CONNECTED_PEER(ifc) && ifc->destination)
{
p = ifc->destination;
addattr_l (&req.n, sizeof req, IFA_BROADCAST, &p->u.prefix,
extern struct thread_master *master;
/* Kernel route reflection. */
-int
+static int
kernel_read (struct thread *thread)
{
int ret;
return 0;
}
+/* Filter out messages from self that occur on listener socket,
+ caused by our actions on the command socket
+ */
+static void netlink_install_filter (int sock, __u32 pid)
+{
+ struct sock_filter filter[] = {
+ /* 0: ldh [4] */
+ BPF_STMT(BPF_LD|BPF_ABS|BPF_H, offsetof(struct nlmsghdr, nlmsg_type)),
+ /* 1: jeq 0x18 jt 3 jf 6 */
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_NEWROUTE), 1, 0),
+ /* 2: jeq 0x19 jt 3 jf 6 */
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htons(RTM_DELROUTE), 0, 3),
+ /* 3: ldw [12] */
+ BPF_STMT(BPF_LD|BPF_ABS|BPF_W, offsetof(struct nlmsghdr, nlmsg_pid)),
+ /* 4: jeq XX jt 5 jf 6 */
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, htonl(pid), 0, 1),
+ /* 5: ret 0 (skip) */
+ BPF_STMT(BPF_RET|BPF_K, 0),
+ /* 6: ret 0xffff (keep) */
+ BPF_STMT(BPF_RET|BPF_K, 0xffff),
+ };
+
+ struct sock_fprog prog = {
+ .len = sizeof(filter) / sizeof(filter[0]),
+ .filter = filter,
+ };
+
+ if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog)) < 0)
+ zlog_warn ("Can't install socket filter: %s\n", safe_strerror(errno));
+}
+
/* Exported interface function. This function simply calls
netlink_socket (). */
void
/* Register kernel socket. */
if (netlink.sock > 0)
- thread_add_read (zebrad.master, kernel_read, NULL, netlink.sock);
+ {
+ /* Only want non-blocking on the netlink event socket */
+ if (fcntl (netlink.sock, F_SETFL, O_NONBLOCK) < 0)
+ zlog (NULL, LOG_ERR, "Can't set %s socket flags: %s", netlink.name,
+ safe_strerror (errno));
+
+ /* Set receive buffer size if it's set from command line */
+ if (nl_rcvbufsize)
+ netlink_recvbuf (&netlink, nl_rcvbufsize);
+
+ netlink_install_filter (netlink.sock, netlink_cmd.snl.nl_pid);
+ thread_add_read (zebrad.master, kernel_read, NULL, netlink.sock);
+ }
}