diff options
Diffstat (limited to 'zebra/kernel_netlink.c')
-rw-r--r-- | zebra/kernel_netlink.c | 171 |
1 files changed, 126 insertions, 45 deletions
diff --git a/zebra/kernel_netlink.c b/zebra/kernel_netlink.c index 7c934ed..8a64a1e 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)); @@ -1219,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. */ @@ -1612,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; @@ -1748,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); @@ -1760,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); @@ -1773,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); @@ -1787,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); @@ -1795,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 @@ -1833,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 @@ -1865,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 @@ -1889,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 */ @@ -1913,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); + } } /* |