summaryrefslogtreecommitdiff
path: root/zebra
diff options
context:
space:
mode:
Diffstat (limited to 'zebra')
-rw-r--r--zebra/rib.h4
-rw-r--r--zebra/rt_netlink.c183
-rw-r--r--zebra/zebra_rib.c93
-rw-r--r--zebra/zserv.c113
-rw-r--r--zebra/zserv.h4
5 files changed, 310 insertions, 87 deletions
diff --git a/zebra/rib.h b/zebra/rib.h
index 2872fc03..1b85c81e 100644
--- a/zebra/rib.h
+++ b/zebra/rib.h
@@ -225,6 +225,10 @@ extern struct nexthop *nexthop_ifname_add (struct rib *, char *);
extern struct nexthop *nexthop_blackhole_add (struct rib *);
extern struct nexthop *nexthop_ipv4_add (struct rib *, struct in_addr *,
struct in_addr *);
+extern struct nexthop *nexthop_ipv4_ifindex_add (struct rib *,
+ struct in_addr *,
+ struct in_addr *,
+ unsigned int);
extern void rib_lookup_and_dump (struct prefix_ipv4 *);
extern void rib_lookup_and_pushup (struct prefix_ipv4 *);
extern void rib_dump (const char *, const struct prefix_ipv4 *, const struct rib *);
diff --git a/zebra/rt_netlink.c b/zebra/rt_netlink.c
index f48df2bf..73097bf6 100644
--- a/zebra/rt_netlink.c
+++ b/zebra/rt_netlink.c
@@ -32,6 +32,7 @@
#include "prefix.h"
#include "connected.h"
#include "table.h"
+#include "memory.h"
#include "rib.h"
#include "thread.h"
#include "privs.h"
@@ -426,6 +427,37 @@ netlink_parse_rtattr (struct rtattr **tb, int max, struct rtattr *rta,
}
}
+/* Utility function to parse hardware link-layer address and update ifp */
+static void
+netlink_interface_update_hw_addr (struct rtattr **tb, struct interface *ifp)
+{
+ int i;
+
+ 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;
+ }
+ }
+}
+
/* Called from interface_lookup_netlink(). This function is only used
during bootstrap. */
static int
@@ -436,7 +468,6 @@ netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h)
struct rtattr *tb[IFLA_MAX + 1];
struct interface *ifp;
char *name;
- int i;
ifi = NLMSG_DATA (h);
@@ -474,30 +505,7 @@ netlink_interface (struct sockaddr_nl *snl, struct nlmsghdr *h)
/* 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;
- }
- }
+ netlink_interface_update_hw_addr (tb, ifp);
if_add_update (ifp);
@@ -709,7 +717,6 @@ netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h)
if (tb[RTA_PREFSRC])
src = RTA_DATA (tb[RTA_PREFSRC]);
- /* Multipath treatment is needed. */
if (tb[RTA_GATEWAY])
gate = RTA_DATA (tb[RTA_GATEWAY]);
@@ -723,7 +730,64 @@ netlink_routing_table (struct sockaddr_nl *snl, struct nlmsghdr *h)
memcpy (&p.prefix, dest, 4);
p.prefixlen = rtm->rtm_dst_len;
- rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, src, index, table, metric, 0, SAFI_UNICAST);
+ if (!tb[RTA_MULTIPATH])
+ rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, src, index,
+ table, metric, 0, SAFI_UNICAST);
+ else
+ {
+ /* This is a multipath route */
+
+ struct rib *rib;
+ struct rtnexthop *rtnh =
+ (struct rtnexthop *) RTA_DATA (tb[RTA_MULTIPATH]);
+
+ len = RTA_PAYLOAD (tb[RTA_MULTIPATH]);
+
+ rib = XCALLOC (MTYPE_RIB, sizeof (struct rib));
+ rib->type = ZEBRA_ROUTE_KERNEL;
+ rib->distance = 0;
+ rib->flags = flags;
+ rib->metric = metric;
+ rib->table = table;
+ rib->nexthop_num = 0;
+ rib->uptime = time (NULL);
+
+ for (;;)
+ {
+ if (len < (int) sizeof (*rtnh) || rtnh->rtnh_len > len)
+ break;
+
+ rib->nexthop_num++;
+ index = rtnh->rtnh_ifindex;
+ gate = 0;
+ if (rtnh->rtnh_len > sizeof (*rtnh))
+ {
+ memset (tb, 0, sizeof (tb));
+ netlink_parse_rtattr (tb, RTA_MAX, RTNH_DATA (rtnh),
+ rtnh->rtnh_len - sizeof (*rtnh));
+ if (tb[RTA_GATEWAY])
+ gate = RTA_DATA (tb[RTA_GATEWAY]);
+ }
+
+ if (gate)
+ {
+ if (index)
+ nexthop_ipv4_ifindex_add (rib, gate, src, index);
+ else
+ nexthop_ipv4_add (rib, gate, src);
+ }
+ else
+ nexthop_ifindex_add (rib, index);
+
+ len -= NLMSG_ALIGN(rtnh->rtnh_len);
+ rtnh = RTNH_NEXT(rtnh);
+ }
+
+ if (rib->nexthop_num == 0)
+ XFREE (MTYPE_RIB, rib);
+ else
+ rib_add_ipv4_multipath (&p, rib, SAFI_UNICAST);
+ }
}
#ifdef HAVE_IPV6
if (rtm->rtm_family == AF_INET6)
@@ -867,7 +931,66 @@ netlink_route_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
}
if (h->nlmsg_type == RTM_NEWROUTE)
- rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, src, index, table, metric, 0, SAFI_UNICAST);
+ {
+ if (!tb[RTA_MULTIPATH])
+ rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, src, index, table,
+ metric, 0, SAFI_UNICAST);
+ else
+ {
+ /* This is a multipath route */
+
+ struct rib *rib;
+ struct rtnexthop *rtnh =
+ (struct rtnexthop *) RTA_DATA (tb[RTA_MULTIPATH]);
+
+ len = RTA_PAYLOAD (tb[RTA_MULTIPATH]);
+
+ rib = XCALLOC (MTYPE_RIB, sizeof (struct rib));
+ rib->type = ZEBRA_ROUTE_KERNEL;
+ rib->distance = 0;
+ rib->flags = 0;
+ rib->metric = metric;
+ rib->table = table;
+ rib->nexthop_num = 0;
+ rib->uptime = time (NULL);
+
+ for (;;)
+ {
+ if (len < (int) sizeof (*rtnh) || rtnh->rtnh_len > len)
+ break;
+
+ rib->nexthop_num++;
+ index = rtnh->rtnh_ifindex;
+ gate = 0;
+ if (rtnh->rtnh_len > sizeof (*rtnh))
+ {
+ memset (tb, 0, sizeof (tb));
+ netlink_parse_rtattr (tb, RTA_MAX, RTNH_DATA (rtnh),
+ rtnh->rtnh_len - sizeof (*rtnh));
+ if (tb[RTA_GATEWAY])
+ gate = RTA_DATA (tb[RTA_GATEWAY]);
+ }
+
+ if (gate)
+ {
+ if (index)
+ nexthop_ipv4_ifindex_add (rib, gate, src, index);
+ else
+ nexthop_ipv4_add (rib, gate, src);
+ }
+ else
+ nexthop_ifindex_add (rib, index);
+
+ len -= NLMSG_ALIGN(rtnh->rtnh_len);
+ rtnh = RTNH_NEXT(rtnh);
+ }
+
+ if (rib->nexthop_num == 0)
+ XFREE (MTYPE_RIB, rib);
+ else
+ rib_add_ipv4_multipath (&p, rib, SAFI_UNICAST);
+ }
+ }
else
rib_delete_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, index, table, SAFI_UNICAST);
}
@@ -960,6 +1083,8 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
ifp->mtu6 = ifp->mtu = *(int *) RTA_DATA (tb[IFLA_MTU]);
ifp->metric = 1;
+ netlink_interface_update_hw_addr (tb, ifp);
+
/* If new link is added. */
if_add_update (ifp);
}
@@ -970,6 +1095,8 @@ netlink_link_change (struct sockaddr_nl *snl, struct nlmsghdr *h)
ifp->mtu6 = ifp->mtu = *(int *) RTA_DATA (tb[IFLA_MTU]);
ifp->metric = 1;
+ netlink_interface_update_hw_addr (tb, ifp);
+
if (if_is_operative (ifp))
{
ifp->flags = ifi->ifi_flags & 0x0000fffff;
diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c
index f7f4d0a2..2fa439c0 100644
--- a/zebra/zebra_rib.c
+++ b/zebra/zebra_rib.c
@@ -232,7 +232,7 @@ nexthop_ipv4_add (struct rib *rib, struct in_addr *ipv4, struct in_addr *src)
return nexthop;
}
-static struct nexthop *
+struct nexthop *
nexthop_ipv4_ifindex_add (struct rib *rib, struct in_addr *ipv4,
struct in_addr *src, unsigned int ifindex)
{
@@ -1279,14 +1279,30 @@ rib_meta_queue_add (struct meta_queue *mq, struct route_node *rn)
static void
rib_queue_add (struct zebra_t *zebra, struct route_node *rn)
{
+ char buf[INET_ADDRSTRLEN];
+ assert (zebra && rn);
if (IS_ZEBRA_DEBUG_RIB_Q)
+ inet_ntop (AF_INET, &rn->p.u.prefix, buf, INET_ADDRSTRLEN);
+
+ /* Pointless to queue a route_node with no RIB entries to add or remove */
+ if (!rn->info)
{
- char buf[INET6_ADDRSTRLEN];
+ zlog_debug ("%s: called for route_node (%p, %d) with no ribs",
+ __func__, rn, rn->lock);
+ zlog_backtrace(LOG_DEBUG);
+ return;
+ }
+
+ if (IS_ZEBRA_DEBUG_RIB_Q)
+ zlog_info ("%s: %s/%d: work queue added", __func__, buf, rn->p.prefixlen);
- zlog_info ("%s: %s/%d: work queue added", __func__,
- inet_ntop (rn->p.family, &rn->p.u.prefix, buf, INET6_ADDRSTRLEN),
- rn->p.prefixlen);
+ assert (zebra);
+
+ if (zebra->ribq == NULL)
+ {
+ zlog_err ("%s: work_queue does not exist!", __func__);
+ return;
}
/*
@@ -1301,6 +1317,11 @@ rib_queue_add (struct zebra_t *zebra, struct route_node *rn)
work_queue_add (zebra->ribq, zebra->mq);
rib_meta_queue_add (zebra->mq, rn);
+
+ if (IS_ZEBRA_DEBUG_RIB_Q)
+ zlog_debug ("%s: %s/%d: rn %p queued", __func__, buf, rn->p.prefixlen, rn);
+
+ return;
}
/* Create new meta queue.
@@ -1328,6 +1349,8 @@ meta_queue_new (void)
static void
rib_queue_init (struct zebra_t *zebra)
{
+ assert (zebra);
+
if (! (zebra->ribq = work_queue_new (zebra->master,
"route_node processing")))
{
@@ -1343,7 +1366,11 @@ rib_queue_init (struct zebra_t *zebra)
zebra->ribq->spec.hold = rib_process_hold_time;
if (!(zebra->mq = meta_queue_new ()))
+ {
zlog_err ("%s: could not initialise meta queue!", __func__);
+ return;
+ }
+ return;
}
/* RIB updates are processed via a queue of pointers to route_nodes.
@@ -2893,6 +2920,62 @@ rib_sweep_route (void)
rib_sweep_table (vrf_table (AFI_IP, SAFI_UNICAST, 0));
rib_sweep_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0));
}
+
+/* Delete routes learned from a given client. */
+/* TODO(wsun) May need to split the sweep process into multiple batches,
+ * so that the process won't take too long if the table is large. */
+static void
+rib_sweep_client_table (struct route_table *table, int rib_type)
+{
+ struct route_node *rn;
+ struct rib *rib;
+ struct rib *next;
+ int ret = 0;
+
+ if (table)
+ for (rn = route_top (table); rn; rn = route_next (rn))
+ for (rib = rn->info; rib; rib = next)
+ {
+ next = rib->next;
+
+ if (CHECK_FLAG (rib->status, RIB_ENTRY_REMOVED))
+ continue;
+
+ if (rib->type == rib_type)
+ if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED))
+ {
+ /* TODO(wsun) Is this mandatory? What about graceful restart/
+ * non-stop forwarding */
+ ret = rib_uninstall_kernel (rn, rib);
+ if (! ret)
+ rib_delnode (rn, rib);
+ else
+ zlog_err ("%s: could not delete routes from kernel!",
+ __func__);
+ }
+ else
+ {
+ /* Always delete the node. */
+ rib_delnode (rn, rib);
+ }
+ }
+}
+
+/* Sweep all routes learned from a given client from RIB tables. */
+void
+rib_sweep_client_route (struct zserv *client)
+{
+ assert(client);
+ int route_type = client->route_type;
+ if (route_type != ZEBRA_ROUTE_MAX)
+ {
+ zlog_debug ("%s: Removing existing routes from client type %d",
+ __func__, route_type);
+ rib_sweep_client_table (vrf_table (AFI_IP, SAFI_UNICAST, 0), route_type);
+ rib_sweep_client_table (vrf_table (AFI_IP6, SAFI_UNICAST, 0), route_type);
+ }
+}
+
/* Remove specific by protocol routes from 'table'. */
static unsigned long
diff --git a/zebra/zserv.c b/zebra/zserv.c
index 672dee88..b1f539d3 100644
--- a/zebra/zserv.c
+++ b/zebra/zserv.c
@@ -140,6 +140,30 @@ zserv_create_header (struct stream *s, uint16_t cmd)
stream_putw (s, cmd);
}
+static void
+zserv_encode_interface (struct stream *s, struct interface *ifp)
+{
+ /* Interface information. */
+ stream_put (s, ifp->name, INTERFACE_NAMSIZ);
+ stream_putl (s, ifp->ifindex);
+ stream_putc (s, ifp->status);
+ stream_putq (s, ifp->flags);
+ stream_putl (s, ifp->metric);
+ stream_putl (s, ifp->mtu);
+ stream_putl (s, ifp->mtu6);
+ stream_putl (s, ifp->bandwidth);
+#ifdef HAVE_STRUCT_SOCKADDR_DL
+ stream_put (s, &ifp->sdl, sizeof (ifp->sdl));
+#else
+ stream_putl (s, ifp->hw_addr_len);
+ if (ifp->hw_addr_len)
+ stream_put (s, ifp->hw_addr, ifp->hw_addr_len);
+#endif /* HAVE_STRUCT_SOCKADDR_DL */
+
+ /* Write packet size. */
+ stream_putw_at (s, 0, stream_get_endp (s));
+}
+
/* Interface is added. Send ZEBRA_INTERFACE_ADD to client. */
/*
* This function is called in the following situations:
@@ -163,28 +187,8 @@ zsend_interface_add (struct zserv *client, struct interface *ifp)
s = client->obuf;
stream_reset (s);
- /* Message type. */
zserv_create_header (s, ZEBRA_INTERFACE_ADD);
-
- /* Interface information. */
- stream_put (s, ifp->name, INTERFACE_NAMSIZ);
- stream_putl (s, ifp->ifindex);
- stream_putc (s, ifp->status);
- stream_putq (s, ifp->flags);
- stream_putl (s, ifp->metric);
- stream_putl (s, ifp->mtu);
- stream_putl (s, ifp->mtu6);
- stream_putl (s, ifp->bandwidth);
-#ifdef HAVE_STRUCT_SOCKADDR_DL
- stream_put (s, &ifp->sdl, sizeof (ifp->sdl));
-#else
- stream_putl (s, ifp->hw_addr_len);
- if (ifp->hw_addr_len)
- stream_put (s, ifp->hw_addr, ifp->hw_addr_len);
-#endif /* HAVE_STRUCT_SOCKADDR_DL */
-
- /* Write packet size. */
- stream_putw_at (s, 0, stream_get_endp (s));
+ zserv_encode_interface (s, ifp);
return zebra_server_send_message(client);
}
@@ -201,21 +205,9 @@ zsend_interface_delete (struct zserv *client, struct interface *ifp)
s = client->obuf;
stream_reset (s);
-
- zserv_create_header (s, ZEBRA_INTERFACE_DELETE);
-
- /* Interface information. */
- stream_put (s, ifp->name, INTERFACE_NAMSIZ);
- stream_putl (s, ifp->ifindex);
- stream_putc (s, ifp->status);
- stream_putq (s, ifp->flags);
- stream_putl (s, ifp->metric);
- stream_putl (s, ifp->mtu);
- stream_putl (s, ifp->mtu6);
- stream_putl (s, ifp->bandwidth);
- /* Write packet length. */
- stream_putw_at (s, 0, stream_get_endp (s));
+ zserv_create_header (s, ZEBRA_INTERFACE_DELETE);
+ zserv_encode_interface (s, ifp);
return zebra_server_send_message (client);
}
@@ -328,19 +320,7 @@ zsend_interface_update (int cmd, struct zserv *client, struct interface *ifp)
stream_reset (s);
zserv_create_header (s, cmd);
-
- /* Interface information. */
- stream_put (s, ifp->name, INTERFACE_NAMSIZ);
- stream_putl (s, ifp->ifindex);
- stream_putc (s, ifp->status);
- stream_putq (s, ifp->flags);
- stream_putl (s, ifp->metric);
- stream_putl (s, ifp->mtu);
- stream_putl (s, ifp->mtu6);
- stream_putl (s, ifp->bandwidth);
-
- /* Write packet size. */
- stream_putw_at (s, 0, stream_get_endp (s));
+ zserv_encode_interface (s, ifp);
return zebra_server_send_message(client);
}
@@ -761,6 +741,13 @@ zread_ipv4_add (struct zserv *client, u_short length)
/* Type, flags, message. */
rib->type = stream_getc (s);
+ /* Update client's route type if it is not done yet. */
+ /* It is done here since only zread_ipv4/6_add() and
+ * zread_ipv4/6_delete() decode Zebra messages and retrieve
+ * route types. */
+ if (client->route_type == ZEBRA_ROUTE_MAX)
+ client->route_type = rib->type;
+
rib->flags = stream_getc (s);
message = stream_getc (s);
safi = stream_getw (s);
@@ -798,10 +785,10 @@ zread_ipv4_add (struct zserv *client, u_short length)
case ZEBRA_NEXTHOP_IPV6:
stream_forward_getp (s, IPV6_MAX_BYTELEN);
break;
- case ZEBRA_NEXTHOP_BLACKHOLE:
- nexthop_blackhole_add (rib);
- break;
- }
+ case ZEBRA_NEXTHOP_BLACKHOLE:
+ nexthop_blackhole_add (rib);
+ break;
+ }
}
}
@@ -826,7 +813,7 @@ zread_ipv4_delete (struct zserv *client, u_short length)
int i;
struct stream *s;
struct zapi_ipv4 api;
- struct in_addr nexthop;
+ struct in_addr nexthop, *nexthop_p;
unsigned long ifindex;
struct prefix_ipv4 p;
u_char nexthop_num;
@@ -836,6 +823,7 @@ zread_ipv4_delete (struct zserv *client, u_short length)
s = client->ibuf;
ifindex = 0;
nexthop.s_addr = 0;
+ nexthop_p = NULL;
/* Type, flags, message. */
api.type = stream_getc (s);
@@ -869,6 +857,7 @@ zread_ipv4_delete (struct zserv *client, u_short length)
break;
case ZEBRA_NEXTHOP_IPV4:
nexthop.s_addr = stream_get_ipv4 (s);
+ nexthop_p = &nexthop;
break;
case ZEBRA_NEXTHOP_IPV6:
stream_forward_getp (s, IPV6_MAX_BYTELEN);
@@ -889,7 +878,7 @@ zread_ipv4_delete (struct zserv *client, u_short length)
else
api.metric = 0;
- rib_delete_ipv4 (api.type, api.flags, &p, &nexthop, ifindex,
+ rib_delete_ipv4 (api.type, api.flags, &p, nexthop_p, ifindex,
client->rtm_table, api.safi);
return 0;
}
@@ -935,6 +924,11 @@ zread_ipv6_add (struct zserv *client, u_short length)
/* Type, flags, message. */
api.type = stream_getc (s);
+ /* Update the route type of the client.
+ * Same as in zread_ipv4_add(). */
+ if (client->route_type == ZEBRA_ROUTE_MAX)
+ client->route_type = api.type;
+
api.flags = stream_getc (s);
api.message = stream_getc (s);
api.safi = stream_getw (s);
@@ -1133,6 +1127,14 @@ zebra_score_rib (int client_sock)
static void
zebra_client_close (struct zserv *client)
{
+ struct stream *s;
+
+ /* Sweep all routes learned from the client first. */
+ rib_sweep_client_route(client);
+ /* Reset the route type. It may not be necessary since the
+ * whole client will be freed. */
+ client->route_type = ZEBRA_ROUTE_MAX;
+
/* Close file descriptor. */
if (client->sock)
{
@@ -1172,6 +1174,9 @@ zebra_client_create (int sock)
/* Make client input/output buffer. */
client->sock = sock;
+ /* Set the default route type to ZEBRA_ROUTE_MAX; it will be updated
+ * once new routes are received. */
+ client->route_type = ZEBRA_ROUTE_MAX;
client->ibuf = stream_new (ZEBRA_MAX_PACKET_SIZ);
client->obuf = stream_new (ZEBRA_MAX_PACKET_SIZ);
client->wb = buffer_new(0);
diff --git a/zebra/zserv.h b/zebra/zserv.h
index 5e8bccac..3d7ebbcd 100644
--- a/zebra/zserv.h
+++ b/zebra/zserv.h
@@ -38,6 +38,10 @@ struct zserv
/* Client file descriptor. */
int sock;
+ /* Client route type. */
+ /* Assuming each client contains only one type of route. */
+ int route_type;
+
/* Input/output buffer to the client. */
struct stream *ibuf;
struct stream *obuf;