From d3e2c74adaa2564775f2c60320213e28e329be50 Mon Sep 17 00:00:00 2001 From: Renato Westphal Date: Thu, 2 Jun 2016 08:28:15 -0300 Subject: [PATCH] mpls: add support to the OpenBSD kernel Signed-off-by: Renato Westphal --- configure.ac | 5 + lib/sockunion.h | 6 ++ zebra/kernel_socket.c | 22 ++++ zebra/kernel_socket.h | 3 +- zebra/rt.h | 1 + zebra/rt_socket.c | 25 ++++- zebra/zebra_mpls.c | 1 + zebra/zebra_mpls_netlink.c | 2 + zebra/zebra_mpls_null.c | 1 + zebra/zebra_mpls_openbsd.c | 206 +++++++++++++++++++++++++++++++++++++ 10 files changed, 270 insertions(+), 2 deletions(-) create mode 100644 zebra/zebra_mpls_openbsd.c diff --git a/configure.ac b/configure.ac index e15f384c2..ac63eaab8 100755 --- a/configure.ac +++ b/configure.ac @@ -374,6 +374,11 @@ if test "x${enable_mpls}" = "xyes"; then MPLS_METHOD="zebra_mpls_netlink.o" AC_MSG_RESULT(Linux MPLS) ;; + *-openbsd*) + AC_DEFINE(HAVE_MPLS,,Enable MPLS) + MPLS_METHOD="zebra_mpls_openbsd.o" + AC_MSG_RESULT(OpenBSD MPLS) + ;; *) AC_MSG_RESULT(Unsupported kernel) MPLS_METHOD="zebra_mpls_null.o" diff --git a/lib/sockunion.h b/lib/sockunion.h index 105b11a24..abad43122 100644 --- a/lib/sockunion.h +++ b/lib/sockunion.h @@ -25,12 +25,18 @@ #include "privs.h" #include "if.h" +#if defined HAVE_MPLS && defined __OpenBSD__ +#include +#endif union sockunion { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; +#if defined HAVE_MPLS && defined __OpenBSD__ + struct sockaddr_mpls smpls; +#endif }; enum connect_result diff --git a/zebra/kernel_socket.c b/zebra/kernel_socket.c index 3a232129b..7952f9e76 100644 --- a/zebra/kernel_socket.c +++ b/zebra/kernel_socket.c @@ -21,6 +21,9 @@ #include #include +#if defined HAVE_MPLS && defined __OpenBSD__ +#include +#endif #include "if.h" #include "prefix.h" @@ -1068,6 +1071,7 @@ rtm_write (int message, union sockunion *dest, union sockunion *mask, union sockunion *gate, + union sockunion *mpls, unsigned int index, int zebra_flags, int metric) @@ -1097,6 +1101,10 @@ rtm_write (int message, msg.rtm.rtm_addrs = RTA_DST; msg.rtm.rtm_addrs |= RTA_GATEWAY; msg.rtm.rtm_flags = RTF_UP; +#if defined HAVE_MPLS && defined __OpenBSD__ + msg.rtm.rtm_flags |= RTF_MPATH; + msg.rtm.rtm_fmask = RTF_MPLS; +#endif msg.rtm.rtm_index = index; if (metric != 0) @@ -1142,6 +1150,17 @@ rtm_write (int message, else if (message == RTM_ADD) msg.rtm.rtm_flags |= RTF_HOST; +#if defined HAVE_MPLS && defined __OpenBSD__ + if (mpls) + { + msg.rtm.rtm_addrs |= RTA_SRC; + msg.rtm.rtm_flags |= RTF_MPLS; + + if (mpls->smpls.smpls_label != htonl (MPLS_IMP_NULL_LABEL << MPLS_LABEL_OFFSET)) + msg.rtm.rtm_mpls = MPLS_OP_PUSH; + } +#endif + /* Tagging route with flags */ msg.rtm.rtm_flags |= (RTF_PROTO1); @@ -1166,6 +1185,9 @@ rtm_write (int message, SOCKADDRSET (dest, RTA_DST); SOCKADDRSET (gate, RTA_GATEWAY); SOCKADDRSET (mask, RTA_NETMASK); +#if defined HAVE_MPLS && defined __OpenBSD__ + SOCKADDRSET (mpls, RTA_SRC); +#endif msg.rtm.rtm_msglen = pnt - (caddr_t) &msg; diff --git a/zebra/kernel_socket.h b/zebra/kernel_socket.h index e9558ad6d..18d69343a 100644 --- a/zebra/kernel_socket.h +++ b/zebra/kernel_socket.h @@ -27,7 +27,8 @@ extern void rtm_read (struct rt_msghdr *); extern int ifam_read (struct ifa_msghdr *); extern int ifm_read (struct if_msghdr *); extern int rtm_write (int, union sockunion *, union sockunion *, - union sockunion *, unsigned int, int, int); + union sockunion *, union sockunion *, + unsigned int, int, int); extern const struct message rtm_type_str[]; #endif /* __ZEBRA_KERNEL_SOCKET_H */ diff --git a/zebra/rt.h b/zebra/rt.h index 1137f880f..c4a85e6d6 100644 --- a/zebra/rt.h +++ b/zebra/rt.h @@ -44,5 +44,6 @@ extern int kernel_delete_ipv6 (struct prefix *, struct rib *); extern int kernel_add_lsp (zebra_lsp_t *); extern int kernel_upd_lsp (zebra_lsp_t *); extern int kernel_del_lsp (zebra_lsp_t *); +extern void mpls_kernel_init (void); #endif /* _ZEBRA_RT_H */ diff --git a/zebra/rt_socket.c b/zebra/rt_socket.c index 24671829f..a6a197806 100644 --- a/zebra/rt_socket.c +++ b/zebra/rt_socket.c @@ -21,6 +21,9 @@ */ #include +#if defined HAVE_MPLS && defined __OpenBSD__ +#include +#endif #include "if.h" #include "prefix.h" @@ -33,13 +36,15 @@ #include "zebra/rib.h" #include "zebra/rt.h" #include "zebra/kernel_socket.h" +#include "zebra/zebra_mpls.h" extern struct zebra_privs_t zserv_privs; /* kernel socket export */ extern int rtm_write (int message, union sockunion *dest, union sockunion *mask, union sockunion *gate, - unsigned int index, int zebra_flags, int metric); + union sockunion *mpls, unsigned int index, + int zebra_flags, int metric); #ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN /* Adjust netmask socket length. Return value is a adjusted sin_len @@ -73,6 +78,10 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) { struct sockaddr_in *mask = NULL; struct sockaddr_in sin_dest, sin_mask, sin_gate; +#if defined HAVE_MPLS && defined __OpenBSD__ + struct sockaddr_mpls smpls; +#endif + union sockunion *smplsp = NULL; struct nexthop *nexthop, *tnexthop; int recursing; int nexthop_num = 0; @@ -147,10 +156,23 @@ kernel_rtm_ipv4 (int cmd, struct prefix *p, struct rib *rib, int family) mask = &sin_mask; } +#if defined HAVE_MPLS && defined __OpenBSD__ + if (nexthop->nh_label) + { + memset (&smpls, 0, sizeof (smpls)); + smpls.smpls_len = sizeof (smpls); + smpls.smpls_family = AF_MPLS; + smpls.smpls_label = + htonl (nexthop->nh_label->label[0] << MPLS_LABEL_OFFSET); + smplsp = (union sockunion *)&smpls; + } +#endif + error = rtm_write (cmd, (union sockunion *)&sin_dest, (union sockunion *)mask, gate ? (union sockunion *)&sin_gate : NULL, + smplsp, ifindex, rib->flags, rib->metric); @@ -365,6 +387,7 @@ kernel_rtm_ipv6_multipath (int cmd, struct prefix *p, struct rib *rib, (union sockunion *) &sin_dest, (union sockunion *) mask, gate ? (union sockunion *)&sin_gate : NULL, + NULL, ifindex, rib->flags, rib->metric); diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 8f18f326d..6b032707a 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -1888,5 +1888,6 @@ zebra_mpls_init_tables (struct zebra_vrf *zvrf) void zebra_mpls_init (void) { + mpls_kernel_init (); mpls_processq_init (&zebrad); } diff --git a/zebra/zebra_mpls_netlink.c b/zebra/zebra_mpls_netlink.c index f5678ede3..4011b90ee 100644 --- a/zebra/zebra_mpls_netlink.c +++ b/zebra/zebra_mpls_netlink.c @@ -76,3 +76,5 @@ kernel_del_lsp (zebra_lsp_t *lsp) return 0; } + +void mpls_kernel_init (void) {}; diff --git a/zebra/zebra_mpls_null.c b/zebra/zebra_mpls_null.c index 9022fe30d..93405b88f 100644 --- a/zebra/zebra_mpls_null.c +++ b/zebra/zebra_mpls_null.c @@ -5,3 +5,4 @@ int kernel_add_lsp (zebra_lsp_t *lsp) { return 0; } int kernel_upd_lsp (zebra_lsp_t *lsp) { return 0; } int kernel_del_lsp (zebra_lsp_t *lsp) { return 0; } +void mpls_kernel_init (void) {}; diff --git a/zebra/zebra_mpls_openbsd.c b/zebra/zebra_mpls_openbsd.c new file mode 100644 index 000000000..43a54adff --- /dev/null +++ b/zebra/zebra_mpls_openbsd.c @@ -0,0 +1,206 @@ +#include +#include +#include "zebra/rt.h" +#include "zebra/zebra_mpls.h" +#include "zebra/debug.h" + +#include "privs.h" +#include "prefix.h" +#include "interface.h" +#include "log.h" + +extern struct zebra_privs_t zserv_privs; + +struct { + u_int32_t rtseq; + int fd; +} kr_state; + +static int +kernel_send_rtmsg (int action, mpls_label_t in_label, zebra_nhlfe_t *nhlfe) +{ + struct iovec iov[5]; + struct rt_msghdr hdr; + struct sockaddr_mpls sa_label_in, sa_label_out; + struct sockaddr_in nexthop; + int iovcnt = 0; + int ret; + + if (IS_ZEBRA_DEBUG_KERNEL) + zlog_debug ("kernel_send_rtmsg: 0x%x, label=%u", action, in_label); + + /* initialize header */ + bzero(&hdr, sizeof (hdr)); + hdr.rtm_version = RTM_VERSION; + + hdr.rtm_type = action; + hdr.rtm_flags = RTF_UP; + hdr.rtm_fmask = RTF_MPLS; + hdr.rtm_seq = kr_state.rtseq++; /* overflow doesn't matter */ + hdr.rtm_msglen = sizeof (hdr); + hdr.rtm_hdrlen = sizeof (struct rt_msghdr); + hdr.rtm_priority = 0; + /* adjust iovec */ + iov[iovcnt].iov_base = &hdr; + iov[iovcnt++].iov_len = sizeof (hdr); + + /* in label */ + bzero(&sa_label_in, sizeof (sa_label_in)); + sa_label_in.smpls_len = sizeof (sa_label_in); + sa_label_in.smpls_family = AF_MPLS; + sa_label_in.smpls_label = htonl(in_label << MPLS_LABEL_OFFSET); + /* adjust header */ + hdr.rtm_flags |= RTF_MPLS | RTF_MPATH; + hdr.rtm_addrs |= RTA_DST; + hdr.rtm_msglen += sizeof (sa_label_in); + /* adjust iovec */ + iov[iovcnt].iov_base = &sa_label_in; + iov[iovcnt++].iov_len = sizeof (sa_label_in); + + /* nexthop */ + bzero(&nexthop, sizeof (nexthop)); + nexthop.sin_len = sizeof (nexthop); + nexthop.sin_family = AF_INET; + nexthop.sin_addr = nhlfe->nexthop->gate.ipv4; + /* adjust header */ + hdr.rtm_flags |= RTF_GATEWAY; + hdr.rtm_addrs |= RTA_GATEWAY; + hdr.rtm_msglen += sizeof (nexthop); + /* adjust iovec */ + iov[iovcnt].iov_base = &nexthop; + iov[iovcnt++].iov_len = sizeof (nexthop); + + /* If action is RTM_DELETE we have to get rid of MPLS infos */ + if (action != RTM_DELETE) + { + bzero(&sa_label_out, sizeof (sa_label_out)); + sa_label_out.smpls_len = sizeof (sa_label_out); + sa_label_out.smpls_family = AF_MPLS; + sa_label_out.smpls_label = + htonl(nhlfe->nexthop->nh_label->label[0] << MPLS_LABEL_OFFSET); + /* adjust header */ + hdr.rtm_addrs |= RTA_SRC; + hdr.rtm_flags |= RTF_MPLS; + hdr.rtm_msglen += sizeof (sa_label_out); + /* adjust iovec */ + iov[iovcnt].iov_base = &sa_label_out; + iov[iovcnt++].iov_len = sizeof (sa_label_out); + + if (nhlfe->nexthop->nh_label->label[0] == MPLS_LABEL_IMPLNULL) + hdr.rtm_mpls = MPLS_OP_POP; + else + hdr.rtm_mpls = MPLS_OP_SWAP; + } + + if (zserv_privs.change(ZPRIVS_RAISE)) + zlog_err ("Can't raise privileges"); + ret = writev (kr_state.fd, iov, iovcnt); + if (zserv_privs.change(ZPRIVS_LOWER)) + zlog_err ("Can't lower privileges"); + + if (ret == -1) + zlog_err ("kernel_send_rtmsg: %s", safe_strerror (errno)); + + return ret; +} + +static int +kernel_lsp_cmd (int action, zebra_lsp_t *lsp) +{ + zebra_nhlfe_t *nhlfe; + struct nexthop *nexthop = NULL; + int nexthop_num = 0; + + for (nhlfe = lsp->nhlfe_list; nhlfe; nhlfe = nhlfe->next) + { + nexthop = nhlfe->nexthop; + if (!nexthop) + continue; + + if (MULTIPATH_NUM != 0 && nexthop_num >= MULTIPATH_NUM) + break; + + /* XXX */ + if (NHLFE_FAMILY(nhlfe) == AF_INET6) + continue; + + if (((action == RTM_ADD || action == RTM_CHANGE) && + (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_SELECTED) && + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE))) || + (action == RTM_DELETE && + (CHECK_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED) && + CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)))) + { + nexthop_num++; + + kernel_send_rtmsg (action, lsp->ile.in_label, nhlfe); + if (action == RTM_ADD || action == RTM_CHANGE) + { + SET_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED); + SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } + else + { + UNSET_FLAG (nhlfe->flags, NHLFE_FLAG_INSTALLED); + UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); + } + } + } + + return (0); +} + +int +kernel_add_lsp (zebra_lsp_t *lsp) +{ + if (!lsp || !lsp->best_nhlfe) // unexpected + return -1; + + return kernel_lsp_cmd (RTM_ADD, lsp); +} + +int +kernel_upd_lsp (zebra_lsp_t *lsp) +{ + if (!lsp || !lsp->best_nhlfe) // unexpected + return -1; + + return kernel_lsp_cmd (RTM_CHANGE, lsp); +} + +int +kernel_del_lsp (zebra_lsp_t *lsp) +{ + if (!lsp) // unexpected + return -1; + + return kernel_lsp_cmd (RTM_DELETE, lsp); +} + +#define MAX_RTSOCK_BUF 128 * 1024 +void +mpls_kernel_init (void) +{ + int rcvbuf, default_rcvbuf; + socklen_t optlen; + + if ((kr_state.fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) { + zlog_warn("kr_init: socket"); + return; + } + + /* grow receive buffer, don't wanna miss messages */ + optlen = sizeof (default_rcvbuf); + if (getsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF, + &default_rcvbuf, &optlen) == -1) + zlog_warn("kr_init getsockopt SOL_SOCKET SO_RCVBUF"); + else + for (rcvbuf = MAX_RTSOCK_BUF; + rcvbuf > default_rcvbuf && + setsockopt(kr_state.fd, SOL_SOCKET, SO_RCVBUF, + &rcvbuf, sizeof (rcvbuf)) == -1 && errno == ENOBUFS; + rcvbuf /= 2) + ; /* nothing */ + + kr_state.rtseq = 1; +} -- 2.39.2