summaryrefslogtreecommitdiff
path: root/zebra/kernel_netlink.c
diff options
context:
space:
mode:
Diffstat (limited to 'zebra/kernel_netlink.c')
-rw-r--r--zebra/kernel_netlink.c257
1 files changed, 142 insertions, 115 deletions
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c
index 78b1dfe276..8a64a1ea48 100644
--- a/zebra/kernel_netlink.c
+++ b/zebra/kernel_netlink.c
@@ -4,8 +4,12 @@
*/
#include <zebra.h>
+#include <fcntl.h>
#ifdef HAVE_NETLINK
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include <linux/filter.h>
#include "linklist.h"
#include "if.h"
@@ -35,6 +39,7 @@
#include "zebra/tc_netlink.h"
#include "zebra/netconf_netlink.h"
#include "zebra/zebra_errors.h"
+#include "zebra/ge_netlink.h"
#ifndef SO_RCVBUFFORCE
#define SO_RCVBUFFORCE (33)
@@ -76,43 +81,48 @@
*/
#define NL_DEFAULT_BATCH_SEND_THRESHOLD (15 * NL_PKT_BUF_SIZE)
-static const struct message nlmsg_str[] = {{RTM_NEWROUTE, "RTM_NEWROUTE"},
- {RTM_DELROUTE, "RTM_DELROUTE"},
- {RTM_GETROUTE, "RTM_GETROUTE"},
- {RTM_NEWLINK, "RTM_NEWLINK"},
- {RTM_SETLINK, "RTM_SETLINK"},
- {RTM_DELLINK, "RTM_DELLINK"},
- {RTM_GETLINK, "RTM_GETLINK"},
- {RTM_NEWADDR, "RTM_NEWADDR"},
- {RTM_DELADDR, "RTM_DELADDR"},
- {RTM_GETADDR, "RTM_GETADDR"},
- {RTM_NEWNEIGH, "RTM_NEWNEIGH"},
- {RTM_DELNEIGH, "RTM_DELNEIGH"},
- {RTM_GETNEIGH, "RTM_GETNEIGH"},
- {RTM_NEWRULE, "RTM_NEWRULE"},
- {RTM_DELRULE, "RTM_DELRULE"},
- {RTM_GETRULE, "RTM_GETRULE"},
- {RTM_NEWNEXTHOP, "RTM_NEWNEXTHOP"},
- {RTM_DELNEXTHOP, "RTM_DELNEXTHOP"},
- {RTM_GETNEXTHOP, "RTM_GETNEXTHOP"},
- {RTM_NEWNETCONF, "RTM_NEWNETCONF"},
- {RTM_DELNETCONF, "RTM_DELNETCONF"},
- {RTM_NEWTUNNEL, "RTM_NEWTUNNEL"},
- {RTM_DELTUNNEL, "RTM_DELTUNNEL"},
- {RTM_GETTUNNEL, "RTM_GETTUNNEL"},
- {RTM_NEWQDISC, "RTM_NEWQDISC"},
- {RTM_DELQDISC, "RTM_DELQDISC"},
- {RTM_GETQDISC, "RTM_GETQDISC"},
- {RTM_NEWTCLASS, "RTM_NEWTCLASS"},
- {RTM_DELTCLASS, "RTM_DELTCLASS"},
- {RTM_GETTCLASS, "RTM_GETTCLASS"},
- {RTM_NEWTFILTER, "RTM_NEWTFILTER"},
- {RTM_DELTFILTER, "RTM_DELTFILTER"},
- {RTM_GETTFILTER, "RTM_GETTFILTER"},
- {RTM_NEWVLAN, "RTM_NEWVLAN"},
- {RTM_DELVLAN, "RTM_DELVLAN"},
- {RTM_GETVLAN, "RTM_GETVLAN"},
- {0}};
+static const struct message nlmsg_str[] = {
+ { RTM_NEWROUTE, "RTM_NEWROUTE" },
+ { RTM_DELROUTE, "RTM_DELROUTE" },
+ { RTM_GETROUTE, "RTM_GETROUTE" },
+ { RTM_NEWLINK, "RTM_NEWLINK" },
+ { RTM_SETLINK, "RTM_SETLINK" },
+ { RTM_DELLINK, "RTM_DELLINK" },
+ { RTM_GETLINK, "RTM_GETLINK" },
+ { RTM_NEWADDR, "RTM_NEWADDR" },
+ { RTM_DELADDR, "RTM_DELADDR" },
+ { RTM_GETADDR, "RTM_GETADDR" },
+ { RTM_NEWNEIGH, "RTM_NEWNEIGH" },
+ { RTM_DELNEIGH, "RTM_DELNEIGH" },
+ { RTM_GETNEIGH, "RTM_GETNEIGH" },
+ { RTM_NEWRULE, "RTM_NEWRULE" },
+ { RTM_DELRULE, "RTM_DELRULE" },
+ { RTM_GETRULE, "RTM_GETRULE" },
+ { RTM_NEWNEXTHOP, "RTM_NEWNEXTHOP" },
+ { RTM_DELNEXTHOP, "RTM_DELNEXTHOP" },
+ { RTM_GETNEXTHOP, "RTM_GETNEXTHOP" },
+ { RTM_NEWNETCONF, "RTM_NEWNETCONF" },
+ { RTM_DELNETCONF, "RTM_DELNETCONF" },
+ { RTM_NEWTUNNEL, "RTM_NEWTUNNEL" },
+ { RTM_DELTUNNEL, "RTM_DELTUNNEL" },
+ { RTM_GETTUNNEL, "RTM_GETTUNNEL" },
+ { RTM_NEWQDISC, "RTM_NEWQDISC" },
+ { RTM_DELQDISC, "RTM_DELQDISC" },
+ { RTM_GETQDISC, "RTM_GETQDISC" },
+ { RTM_NEWTCLASS, "RTM_NEWTCLASS" },
+ { RTM_DELTCLASS, "RTM_DELTCLASS" },
+ { RTM_GETTCLASS, "RTM_GETTCLASS" },
+ { RTM_NEWTFILTER, "RTM_NEWTFILTER" },
+ { RTM_DELTFILTER, "RTM_DELTFILTER" },
+ { RTM_GETTFILTER, "RTM_GETTFILTER" },
+ { RTM_NEWVLAN, "RTM_NEWVLAN" },
+ { RTM_DELVLAN, "RTM_DELVLAN" },
+ { RTM_GETVLAN, "RTM_GETVLAN" },
+ { RTM_NEWCHAIN, "RTM_NEWCHAIN" },
+ { RTM_DELCHAIN, "RTM_DELCHAIN" },
+ { RTM_GETCHAIN, "RTM_GETCHAIN" },
+ { 0 }
+};
static const struct message rtproto_str[] = {
{RTPROT_REDIRECT, "redirect"},
@@ -301,7 +311,7 @@ static const char *group2str(uint32_t group)
/* Make socket for Linux netlink interface. */
static int netlink_socket(struct nlsock *nl, unsigned long groups,
uint32_t ext_groups[], uint8_t ext_group_size,
- ns_id_t ns_id)
+ ns_id_t ns_id, int nl_family)
{
int ret;
struct sockaddr_nl snl;
@@ -309,7 +319,7 @@ static int netlink_socket(struct nlsock *nl, unsigned long groups,
int namelen;
frr_with_privs(&zserv_privs) {
- sock = ns_socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE, ns_id);
+ sock = ns_socket(AF_NETLINK, SOCK_RAW, nl_family, ns_id);
if (sock < 0) {
zlog_err("Can't open %s socket: %s", nl->name,
safe_strerror(errno));
@@ -398,7 +408,7 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id,
case RTM_NEWLINK:
return netlink_link_change(h, ns_id, startup);
case RTM_DELLINK:
- return netlink_link_change(h, ns_id, startup);
+ return 0;
case RTM_NEWNEIGH:
case RTM_DELNEIGH:
case RTM_GETNEIGH:
@@ -425,6 +435,12 @@ static int netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id,
case RTM_DELVLAN:
return netlink_vlan_change(h, ns_id, startup);
+ /* Messages we may receive, but ignore */
+ case RTM_NEWCHAIN:
+ case RTM_DELCHAIN:
+ case RTM_GETCHAIN:
+ return 0;
+
/* Messages handled in the dplane thread */
case RTM_NEWADDR:
case RTM_DELADDR:
@@ -474,6 +490,7 @@ static int dplane_netlink_information_fetch(struct nlmsghdr *h, ns_id_t ns_id,
case RTM_NEWLINK:
case RTM_DELLINK:
+ return netlink_link_change(h, ns_id, startup);
default:
break;
@@ -740,58 +757,6 @@ void nl_attr_rtnh_end(struct nlmsghdr *n, struct rtnexthop *rtnh)
rtnh->rtnh_len = (uint8_t *)NLMSG_TAIL(n) - (uint8_t *)rtnh;
}
-bool nl_rta_put(struct rtattr *rta, unsigned int maxlen, int type,
- const void *data, int alen)
-{
- struct rtattr *subrta;
- int len = RTA_LENGTH(alen);
-
- if (RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len) > maxlen) {
- zlog_err("ERROR max allowed bound %d exceeded for rtattr",
- maxlen);
- return false;
- }
- subrta = (struct rtattr *)(((char *)rta) + RTA_ALIGN(rta->rta_len));
- subrta->rta_type = type;
- subrta->rta_len = len;
- if (alen)
- memcpy(RTA_DATA(subrta), data, alen);
- rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len);
-
- return true;
-}
-
-bool nl_rta_put16(struct rtattr *rta, unsigned int maxlen, int type,
- uint16_t data)
-{
- return nl_rta_put(rta, maxlen, type, &data, sizeof(uint16_t));
-}
-
-bool nl_rta_put64(struct rtattr *rta, unsigned int maxlen, int type,
- uint64_t data)
-{
- return nl_rta_put(rta, maxlen, type, &data, sizeof(uint64_t));
-}
-
-struct rtattr *nl_rta_nest(struct rtattr *rta, unsigned int maxlen, int type)
-{
- struct rtattr *nest = RTA_TAIL(rta);
-
- if (nl_rta_put(rta, maxlen, type, NULL, 0))
- return NULL;
-
- nest->rta_type |= NLA_F_NESTED;
-
- return nest;
-}
-
-int nl_rta_nest_end(struct rtattr *rta, struct rtattr *nest)
-{
- nest->rta_len = (uint8_t *)RTA_TAIL(rta) - (uint8_t *)nest;
-
- return rta->rta_len;
-}
-
const char *nl_msg_type_to_str(uint16_t msg_type)
{
return lookup_msg(nlmsg_str, msg_type, "");
@@ -1169,7 +1134,6 @@ int netlink_parse_info(int (*filter)(struct nlmsghdr *, ns_id_t, int),
h->nlmsg_type, h->nlmsg_len,
h->nlmsg_seq, h->nlmsg_pid);
-
/*
* Ignore messages that maybe sent from
* other actors besides the kernel
@@ -1265,6 +1229,33 @@ int netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
return netlink_talk_info(filter, n, &dp_info, startup);
}
+/*
+ * Synchronous version of netlink_talk_info. Converts args to suit the
+ * common version, which is suitable for both sync and async use.
+ */
+int ge_netlink_talk(int (*filter)(struct nlmsghdr *, ns_id_t, int startup),
+ struct nlmsghdr *n, struct zebra_ns *zns, bool startup)
+{
+ struct zebra_dplane_info dp_info;
+
+ if (zns->ge_netlink_cmd.sock < 0)
+ return -1;
+
+ /* Increment sequence number before capturing snapshot of ns socket
+ * info.
+ */
+ zns->ge_netlink_cmd.seq = zebra_router_get_next_sequence();
+
+ /* Capture info in intermediate info struct */
+ dp_info.ns_id = zns->ns_id;
+
+ dp_info.is_cmd = true;
+ dp_info.sock = zns->ge_netlink_cmd.sock;
+ dp_info.seq = zns->ge_netlink_cmd.seq;
+
+ return netlink_talk_info(filter, n, &dp_info, startup);
+}
+
/* Issue request message to kernel via netlink socket. GET messages
* are issued through this interface.
*/
@@ -1289,18 +1280,15 @@ int netlink_request(struct nlsock *nl, void *req)
return 0;
}
-static int nl_batch_read_resp(struct nl_batch *bth)
+static int nl_batch_read_resp(struct nl_batch *bth, struct nlsock *nl)
{
struct nlmsghdr *h;
struct sockaddr_nl snl;
struct msghdr msg = {};
int status, seq;
- struct nlsock *nl;
struct zebra_dplane_ctx *ctx;
bool ignore_msg;
- nl = kernel_netlink_nlsock_lookup(bth->zns->sock);
-
msg.msg_name = (void *)&snl;
msg.msg_namelen = sizeof(snl);
@@ -1493,7 +1481,7 @@ static void nl_batch_send(struct nl_batch *bth)
err = true;
if (!err) {
- if (nl_batch_read_resp(bth) == -1)
+ if (nl_batch_read_resp(bth, nl) == -1)
err = true;
}
}
@@ -1631,6 +1619,7 @@ static enum netlink_msg_status nl_put_msg(struct nl_batch *bth,
case DPLANE_OP_IPSET_DELETE:
case DPLANE_OP_IPSET_ENTRY_ADD:
case DPLANE_OP_IPSET_ENTRY_DELETE:
+ case DPLANE_OP_STARTUP_STAGE:
return FRR_NETLINK_ERROR;
case DPLANE_OP_GRE_SET:
@@ -1660,6 +1649,9 @@ static enum netlink_msg_status nl_put_msg(struct nl_batch *bth,
case DPLANE_OP_TC_FILTER_DELETE:
case DPLANE_OP_TC_FILTER_UPDATE:
return netlink_put_tc_filter_update_msg(bth, ctx);
+
+ case DPLANE_OP_SRV6_ENCAP_SRCADDR_SET:
+ return netlink_put_sr_tunsrc_set_msg(bth, ctx);
}
return FRR_NETLINK_ERROR;
@@ -1777,17 +1769,11 @@ void kernel_init(struct zebra_ns *zns)
* groups are added further below after SOL_NETLINK is verified to
* exist.
*/
- groups = RTMGRP_LINK |
- RTMGRP_IPV4_ROUTE |
- RTMGRP_IPV4_IFADDR |
- RTMGRP_IPV6_ROUTE |
- RTMGRP_IPV6_IFADDR |
- RTMGRP_IPV4_MROUTE |
- RTMGRP_NEIGH |
- ((uint32_t) 1 << (RTNLGRP_IPV4_RULE - 1)) |
- ((uint32_t) 1 << (RTNLGRP_IPV6_RULE - 1)) |
- ((uint32_t) 1 << (RTNLGRP_NEXTHOP - 1)) |
- ((uint32_t) 1 << (RTNLGRP_TC - 1));
+ groups = RTMGRP_IPV4_ROUTE | RTMGRP_IPV6_ROUTE | RTMGRP_IPV4_MROUTE |
+ RTMGRP_NEIGH | ((uint32_t)1 << (RTNLGRP_IPV4_RULE - 1)) |
+ ((uint32_t)1 << (RTNLGRP_IPV6_RULE - 1)) |
+ ((uint32_t)1 << (RTNLGRP_NEXTHOP - 1)) |
+ ((uint32_t)1 << (RTNLGRP_TC - 1));
dplane_groups = (RTMGRP_LINK |
RTMGRP_IPV4_IFADDR |
@@ -1802,8 +1788,8 @@ void kernel_init(struct zebra_ns *zns)
snprintf(zns->netlink.name, sizeof(zns->netlink.name),
"netlink-listen (NS %u)", zns->ns_id);
zns->netlink.sock = -1;
- if (netlink_socket(&zns->netlink, groups, &ext_groups, 1, zns->ns_id) <
- 0) {
+ if (netlink_socket(&zns->netlink, groups, &ext_groups, 1, zns->ns_id,
+ NETLINK_ROUTE) < 0) {
zlog_err("Failure to create %s socket",
zns->netlink.name);
exit(-1);
@@ -1814,7 +1800,8 @@ void kernel_init(struct zebra_ns *zns)
snprintf(zns->netlink_cmd.name, sizeof(zns->netlink_cmd.name),
"netlink-cmd (NS %u)", zns->ns_id);
zns->netlink_cmd.sock = -1;
- if (netlink_socket(&zns->netlink_cmd, 0, 0, 0, zns->ns_id) < 0) {
+ if (netlink_socket(&zns->netlink_cmd, 0, 0, 0, zns->ns_id,
+ NETLINK_ROUTE) < 0) {
zlog_err("Failure to create %s socket",
zns->netlink_cmd.name);
exit(-1);
@@ -1827,7 +1814,8 @@ void kernel_init(struct zebra_ns *zns)
sizeof(zns->netlink_dplane_out.name), "netlink-dp (NS %u)",
zns->ns_id);
zns->netlink_dplane_out.sock = -1;
- if (netlink_socket(&zns->netlink_dplane_out, 0, 0, 0, zns->ns_id) < 0) {
+ if (netlink_socket(&zns->netlink_dplane_out, 0, 0, 0, zns->ns_id,
+ NETLINK_ROUTE) < 0) {
zlog_err("Failure to create %s socket",
zns->netlink_dplane_out.name);
exit(-1);
@@ -1841,7 +1829,7 @@ void kernel_init(struct zebra_ns *zns)
zns->ns_id);
zns->netlink_dplane_in.sock = -1;
if (netlink_socket(&zns->netlink_dplane_in, dplane_groups, 0, 0,
- zns->ns_id) < 0) {
+ zns->ns_id, NETLINK_ROUTE) < 0) {
zlog_err("Failure to create %s socket",
zns->netlink_dplane_in.name);
exit(-1);
@@ -1849,6 +1837,19 @@ void kernel_init(struct zebra_ns *zns)
kernel_netlink_nlsock_insert(&zns->netlink_dplane_in);
+ /* Generic Netlink socket. */
+ snprintf(zns->ge_netlink_cmd.name, sizeof(zns->ge_netlink_cmd.name),
+ "generic-netlink-cmd (NS %u)", zns->ns_id);
+ zns->ge_netlink_cmd.sock = -1;
+ if (netlink_socket(&zns->ge_netlink_cmd, 0, 0, 0, zns->ns_id,
+ NETLINK_GENERIC) < 0) {
+ zlog_warn("Failure to create %s socket",
+ zns->ge_netlink_cmd.name);
+ }
+
+ if (zns->ge_netlink_cmd.sock >= 0)
+ kernel_netlink_nlsock_insert(&zns->ge_netlink_cmd);
+
/*
* SOL_NETLINK is not available on all platforms yet
* apparently. It's in bits/socket.h which I am not
@@ -1887,6 +1888,15 @@ void kernel_init(struct zebra_ns *zns)
zlog_notice("Registration for extended dp ACK failed : %d %s",
errno, safe_strerror(errno));
+ if (zns->ge_netlink_cmd.sock >= 0) {
+ one = 1;
+ ret = setsockopt(zns->ge_netlink_cmd.sock, SOL_NETLINK,
+ NETLINK_EXT_ACK, &one, sizeof(one));
+ if (ret < 0)
+ zlog_err("Registration for extended generic netlink cmd ACK failed : %d %s",
+ errno, safe_strerror(errno));
+ }
+
/*
* Trim off the payload of the original netlink message in the
* acknowledgment. This option is available since Linux 4.2, so if
@@ -1919,12 +1929,22 @@ void kernel_init(struct zebra_ns *zns)
zns->netlink_dplane_in.name, safe_strerror(errno),
errno);
+ if (zns->ge_netlink_cmd.sock >= 0) {
+ if (fcntl(zns->ge_netlink_cmd.sock, F_SETFL, O_NONBLOCK) < 0)
+ zlog_err("Can't set %s socket error: %s(%d)",
+ zns->ge_netlink_cmd.name, safe_strerror(errno),
+ errno);
+ }
+
/* Set receive buffer size if it's set from command line */
if (rcvbufsize) {
netlink_recvbuf(&zns->netlink, rcvbufsize);
netlink_recvbuf(&zns->netlink_cmd, rcvbufsize);
netlink_recvbuf(&zns->netlink_dplane_out, rcvbufsize);
netlink_recvbuf(&zns->netlink_dplane_in, rcvbufsize);
+
+ if (zns->ge_netlink_cmd.sock >= 0)
+ netlink_recvbuf(&zns->ge_netlink_cmd, rcvbufsize);
}
/* Set filter for inbound sockets, to exclude events we've generated
@@ -1943,6 +1963,8 @@ void kernel_init(struct zebra_ns *zns)
&zns->t_netlink);
rt_netlink_init();
+
+ ge_netlink_init(zns);
}
/* Helper to clean up an nlsock */
@@ -1967,11 +1989,16 @@ void kernel_terminate(struct zebra_ns *zns, bool complete)
kernel_nlsock_fini(&zns->netlink_dplane_in);
+ kernel_nlsock_fini(&zns->ge_netlink_cmd);
+
/* During zebra shutdown, we need to leave the dataplane socket
* around until all work is done.
*/
- if (complete)
+ if (complete) {
kernel_nlsock_fini(&zns->netlink_dplane_out);
+
+ XFREE(MTYPE_NL_BUF, nl_batch_tx_buf);
+ }
}
/*