1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2016 by Open Source Routing.
10 #include <netmpls/mpls.h>
12 #include "zebra/zebra_mpls.h"
13 #include "zebra/debug.h"
14 #include "zebra/zebra_errors.h"
15 #include "zebra/zebra_router.h"
19 #include "interface.h"
21 #include "lib_errors.h"
23 extern struct zebra_privs_t zserv_privs
;
31 static int kernel_send_rtmsg_v4(int action
, mpls_label_t in_label
,
32 const struct zebra_nhlfe
*nhlfe
)
36 struct sockaddr_mpls sa_label_in
, sa_label_out
;
37 struct sockaddr_in nexthop
;
41 if (IS_ZEBRA_DEBUG_KERNEL
)
42 zlog_debug("%s: 0x%x, label=%u", __func__
, action
, in_label
);
44 /* initialize header */
45 memset(&hdr
, 0, sizeof(hdr
));
46 hdr
.rtm_version
= RTM_VERSION
;
48 hdr
.rtm_type
= action
;
49 hdr
.rtm_flags
= RTF_UP
;
50 hdr
.rtm_fmask
= RTF_MPLS
;
51 hdr
.rtm_seq
= kr_state
.rtseq
++; /* overflow doesn't matter */
52 hdr
.rtm_msglen
= sizeof(hdr
);
53 hdr
.rtm_hdrlen
= sizeof(struct rt_msghdr
);
56 iov
[iovcnt
].iov_base
= &hdr
;
57 iov
[iovcnt
++].iov_len
= sizeof(hdr
);
60 memset(&sa_label_in
, 0, sizeof(sa_label_in
));
61 sa_label_in
.smpls_len
= sizeof(sa_label_in
);
62 sa_label_in
.smpls_family
= AF_MPLS
;
63 sa_label_in
.smpls_label
= htonl(in_label
<< MPLS_LABEL_OFFSET
);
65 hdr
.rtm_flags
|= RTF_MPLS
| RTF_MPATH
;
66 hdr
.rtm_addrs
|= RTA_DST
;
67 hdr
.rtm_msglen
+= sizeof(sa_label_in
);
69 iov
[iovcnt
].iov_base
= &sa_label_in
;
70 iov
[iovcnt
++].iov_len
= sizeof(sa_label_in
);
73 memset(&nexthop
, 0, sizeof(nexthop
));
74 nexthop
.sin_len
= sizeof(nexthop
);
75 nexthop
.sin_family
= AF_INET
;
76 nexthop
.sin_addr
= nhlfe
->nexthop
->gate
.ipv4
;
78 hdr
.rtm_flags
|= RTF_GATEWAY
;
79 hdr
.rtm_addrs
|= RTA_GATEWAY
;
80 hdr
.rtm_msglen
+= sizeof(nexthop
);
82 iov
[iovcnt
].iov_base
= &nexthop
;
83 iov
[iovcnt
++].iov_len
= sizeof(nexthop
);
85 /* If action is RTM_DELETE we have to get rid of MPLS infos */
86 if (action
!= RTM_DELETE
) {
87 memset(&sa_label_out
, 0, sizeof(sa_label_out
));
88 sa_label_out
.smpls_len
= sizeof(sa_label_out
);
89 sa_label_out
.smpls_family
= AF_MPLS
;
90 sa_label_out
.smpls_label
=
91 htonl(nhlfe
->nexthop
->nh_label
->label
[0]
92 << MPLS_LABEL_OFFSET
);
94 hdr
.rtm_addrs
|= RTA_SRC
;
95 hdr
.rtm_flags
|= RTF_MPLS
;
96 hdr
.rtm_msglen
+= sizeof(sa_label_out
);
98 iov
[iovcnt
].iov_base
= &sa_label_out
;
99 iov
[iovcnt
++].iov_len
= sizeof(sa_label_out
);
101 if (nhlfe
->nexthop
->nh_label
->label
[0] == MPLS_LABEL_IMPLNULL
)
102 hdr
.rtm_mpls
= MPLS_OP_POP
;
104 hdr
.rtm_mpls
= MPLS_OP_SWAP
;
107 frr_with_privs(&zserv_privs
) {
108 ret
= writev(kr_state
.fd
, iov
, iovcnt
);
112 flog_err_sys(EC_LIB_SOCKET
, "%s: %s", __func__
,
113 safe_strerror(errno
));
118 #if !defined(ROUNDUP)
120 (((a) & (sizeof(long) - 1)) ? (1 + ((a) | (sizeof(long) - 1))) : (a))
123 static int kernel_send_rtmsg_v6(int action
, mpls_label_t in_label
,
124 const struct zebra_nhlfe
*nhlfe
)
127 struct rt_msghdr hdr
;
128 struct sockaddr_mpls sa_label_in
, sa_label_out
;
130 struct sockaddr_in6 addr
;
131 char pad
[sizeof(long)]; /* thank you IPv6 */
136 if (IS_ZEBRA_DEBUG_KERNEL
)
137 zlog_debug("%s: 0x%x, label=%u", __func__
, action
, in_label
);
139 /* initialize header */
140 memset(&hdr
, 0, sizeof(hdr
));
141 hdr
.rtm_version
= RTM_VERSION
;
143 hdr
.rtm_type
= action
;
144 hdr
.rtm_flags
= RTF_UP
;
145 hdr
.rtm_fmask
= RTF_MPLS
;
146 hdr
.rtm_seq
= kr_state
.rtseq
++; /* overflow doesn't matter */
147 hdr
.rtm_msglen
= sizeof(hdr
);
148 hdr
.rtm_hdrlen
= sizeof(struct rt_msghdr
);
149 hdr
.rtm_priority
= 0;
151 iov
[iovcnt
].iov_base
= &hdr
;
152 iov
[iovcnt
++].iov_len
= sizeof(hdr
);
155 memset(&sa_label_in
, 0, sizeof(sa_label_in
));
156 sa_label_in
.smpls_len
= sizeof(sa_label_in
);
157 sa_label_in
.smpls_family
= AF_MPLS
;
158 sa_label_in
.smpls_label
= htonl(in_label
<< MPLS_LABEL_OFFSET
);
160 hdr
.rtm_flags
|= RTF_MPLS
| RTF_MPATH
;
161 hdr
.rtm_addrs
|= RTA_DST
;
162 hdr
.rtm_msglen
+= sizeof(sa_label_in
);
164 iov
[iovcnt
].iov_base
= &sa_label_in
;
165 iov
[iovcnt
++].iov_len
= sizeof(sa_label_in
);
168 memset(&nexthop
, 0, sizeof(nexthop
));
169 nexthop
.addr
.sin6_len
= sizeof(struct sockaddr_in6
);
170 nexthop
.addr
.sin6_family
= AF_INET6
;
171 nexthop
.addr
.sin6_addr
= nhlfe
->nexthop
->gate
.ipv6
;
172 if (IN6_IS_ADDR_LINKLOCAL(&nexthop
.addr
.sin6_addr
)) {
174 struct sockaddr_in6
*sin6
= &nexthop
.addr
;
176 nexthop
.addr
.sin6_scope_id
= nhlfe
->nexthop
->ifindex
;
178 memcpy(&tmp16
, &sin6
->sin6_addr
.s6_addr
[2], sizeof(tmp16
));
179 tmp16
= htons(sin6
->sin6_scope_id
);
180 memcpy(&sin6
->sin6_addr
.s6_addr
[2], &tmp16
, sizeof(tmp16
));
181 sin6
->sin6_scope_id
= 0;
185 hdr
.rtm_flags
|= RTF_GATEWAY
;
186 hdr
.rtm_addrs
|= RTA_GATEWAY
;
187 hdr
.rtm_msglen
+= ROUNDUP(sizeof(struct sockaddr_in6
));
189 iov
[iovcnt
].iov_base
= &nexthop
;
190 iov
[iovcnt
++].iov_len
= ROUNDUP(sizeof(struct sockaddr_in6
));
192 /* If action is RTM_DELETE we have to get rid of MPLS infos */
193 if (action
!= RTM_DELETE
) {
194 memset(&sa_label_out
, 0, sizeof(sa_label_out
));
195 sa_label_out
.smpls_len
= sizeof(sa_label_out
);
196 sa_label_out
.smpls_family
= AF_MPLS
;
197 sa_label_out
.smpls_label
=
198 htonl(nhlfe
->nexthop
->nh_label
->label
[0]
199 << MPLS_LABEL_OFFSET
);
201 hdr
.rtm_addrs
|= RTA_SRC
;
202 hdr
.rtm_flags
|= RTF_MPLS
;
203 hdr
.rtm_msglen
+= sizeof(sa_label_out
);
205 iov
[iovcnt
].iov_base
= &sa_label_out
;
206 iov
[iovcnt
++].iov_len
= sizeof(sa_label_out
);
208 if (nhlfe
->nexthop
->nh_label
->label
[0] == MPLS_LABEL_IMPLNULL
)
209 hdr
.rtm_mpls
= MPLS_OP_POP
;
211 hdr
.rtm_mpls
= MPLS_OP_SWAP
;
214 frr_with_privs(&zserv_privs
) {
215 ret
= writev(kr_state
.fd
, iov
, iovcnt
);
219 flog_err_sys(EC_LIB_SOCKET
, "%s: %s", __func__
,
220 safe_strerror(errno
));
225 static int kernel_lsp_cmd(struct zebra_dplane_ctx
*ctx
)
227 const struct nhlfe_list_head
*head
;
228 const struct zebra_nhlfe
*nhlfe
;
229 const struct nexthop
*nexthop
= NULL
;
230 unsigned int nexthop_num
= 0;
233 switch (dplane_ctx_get_op(ctx
)) {
234 case DPLANE_OP_LSP_DELETE
:
237 case DPLANE_OP_LSP_INSTALL
:
240 case DPLANE_OP_LSP_UPDATE
:
244 case DPLANE_OP_ROUTE_INSTALL
:
245 case DPLANE_OP_ROUTE_UPDATE
:
246 case DPLANE_OP_ROUTE_DELETE
:
247 case DPLANE_OP_ROUTE_NOTIFY
:
248 case DPLANE_OP_NH_INSTALL
:
249 case DPLANE_OP_NH_UPDATE
:
250 case DPLANE_OP_NH_DELETE
:
251 case DPLANE_OP_LSP_NOTIFY
:
252 case DPLANE_OP_PW_INSTALL
:
253 case DPLANE_OP_PW_UNINSTALL
:
254 case DPLANE_OP_SYS_ROUTE_ADD
:
255 case DPLANE_OP_SYS_ROUTE_DELETE
:
256 case DPLANE_OP_ADDR_INSTALL
:
257 case DPLANE_OP_ADDR_UNINSTALL
:
258 case DPLANE_OP_MAC_INSTALL
:
259 case DPLANE_OP_MAC_DELETE
:
260 case DPLANE_OP_NEIGH_INSTALL
:
261 case DPLANE_OP_NEIGH_UPDATE
:
262 case DPLANE_OP_NEIGH_DELETE
:
263 case DPLANE_OP_VTEP_ADD
:
264 case DPLANE_OP_VTEP_DELETE
:
265 case DPLANE_OP_RULE_ADD
:
266 case DPLANE_OP_RULE_DELETE
:
267 case DPLANE_OP_RULE_UPDATE
:
268 case DPLANE_OP_NEIGH_DISCOVER
:
269 case DPLANE_OP_BR_PORT_UPDATE
:
270 case DPLANE_OP_IPTABLE_ADD
:
271 case DPLANE_OP_IPTABLE_DELETE
:
272 case DPLANE_OP_IPSET_ADD
:
273 case DPLANE_OP_IPSET_DELETE
:
274 case DPLANE_OP_IPSET_ENTRY_ADD
:
275 case DPLANE_OP_IPSET_ENTRY_DELETE
:
276 case DPLANE_OP_NEIGH_IP_INSTALL
:
277 case DPLANE_OP_NEIGH_IP_DELETE
:
278 case DPLANE_OP_NEIGH_TABLE_UPDATE
:
279 case DPLANE_OP_GRE_SET
:
280 case DPLANE_OP_INTF_ADDR_ADD
:
281 case DPLANE_OP_INTF_ADDR_DEL
:
282 case DPLANE_OP_INTF_NETCONFIG
:
283 case DPLANE_OP_INTF_INSTALL
:
284 case DPLANE_OP_INTF_UPDATE
:
285 case DPLANE_OP_INTF_DELETE
:
286 case DPLANE_OP_TC_QDISC_INSTALL
:
287 case DPLANE_OP_TC_QDISC_UNINSTALL
:
288 case DPLANE_OP_TC_CLASS_ADD
:
289 case DPLANE_OP_TC_CLASS_DELETE
:
290 case DPLANE_OP_TC_CLASS_UPDATE
:
291 case DPLANE_OP_TC_FILTER_ADD
:
292 case DPLANE_OP_TC_FILTER_DELETE
:
293 case DPLANE_OP_TC_FILTER_UPDATE
:
297 head
= dplane_ctx_get_nhlfe_list(ctx
);
298 frr_each(nhlfe_list_const
, head
, nhlfe
) {
299 nexthop
= nhlfe
->nexthop
;
303 if (nexthop_num
>= zrouter
.multipath_num
)
306 if (((action
== RTM_ADD
|| action
== RTM_CHANGE
)
307 && (CHECK_FLAG(nhlfe
->flags
, NHLFE_FLAG_SELECTED
)
308 && CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
)))
309 || (action
== RTM_DELETE
310 && (CHECK_FLAG(nhlfe
->flags
, NHLFE_FLAG_INSTALLED
)
311 && CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_FIB
)))) {
312 if (nhlfe
->nexthop
->nh_label
->num_labels
> 1) {
313 flog_warn(EC_ZEBRA_MAX_LABELS_PUSH
,
314 "%s: can't push %u labels at once (maximum is 1)",
316 nhlfe
->nexthop
->nh_label
->num_labels
);
322 switch (NHLFE_FAMILY(nhlfe
)) {
324 kernel_send_rtmsg_v4(
326 dplane_ctx_get_in_label(ctx
),
330 kernel_send_rtmsg_v6(
332 dplane_ctx_get_in_label(ctx
),
344 enum zebra_dplane_result
kernel_lsp_update(struct zebra_dplane_ctx
*ctx
)
348 ret
= kernel_lsp_cmd(ctx
);
351 ZEBRA_DPLANE_REQUEST_SUCCESS
: ZEBRA_DPLANE_REQUEST_FAILURE
);
354 static enum zebra_dplane_result
kmpw_install(struct zebra_dplane_ctx
*ctx
)
358 struct sockaddr_storage ss
;
359 struct sockaddr_in
*sa_in
= (struct sockaddr_in
*)&ss
;
360 struct sockaddr_in6
*sa_in6
= (struct sockaddr_in6
*)&ss
;
361 const union g_addr
*gaddr
;
363 memset(&imr
, 0, sizeof(imr
));
364 switch (dplane_ctx_get_pw_type(ctx
)) {
365 case PW_TYPE_ETHERNET
:
366 imr
.imr_type
= IMR_TYPE_ETHERNET
;
368 case PW_TYPE_ETHERNET_TAGGED
:
369 imr
.imr_type
= IMR_TYPE_ETHERNET_TAGGED
;
372 zlog_debug("%s: unhandled pseudowire type (%#X)", __func__
,
373 dplane_ctx_get_pw_type(ctx
));
374 return ZEBRA_DPLANE_REQUEST_FAILURE
;
377 if (dplane_ctx_get_pw_flags(ctx
) & F_PSEUDOWIRE_CWORD
)
378 imr
.imr_flags
|= IMR_FLAG_CONTROLWORD
;
380 /* pseudowire nexthop */
381 memset(&ss
, 0, sizeof(ss
));
382 gaddr
= dplane_ctx_get_pw_dest(ctx
);
383 switch (dplane_ctx_get_pw_af(ctx
)) {
385 sa_in
->sin_family
= AF_INET
;
386 sa_in
->sin_len
= sizeof(struct sockaddr_in
);
387 sa_in
->sin_addr
= gaddr
->ipv4
;
390 sa_in6
->sin6_family
= AF_INET6
;
391 sa_in6
->sin6_len
= sizeof(struct sockaddr_in6
);
392 sa_in6
->sin6_addr
= gaddr
->ipv6
;
395 zlog_debug("%s: unhandled pseudowire address-family (%u)",
396 __func__
, dplane_ctx_get_pw_af(ctx
));
397 return ZEBRA_DPLANE_REQUEST_FAILURE
;
399 memcpy(&imr
.imr_nexthop
, (struct sockaddr
*)&ss
,
400 sizeof(imr
.imr_nexthop
));
402 /* pseudowire local/remote labels */
403 imr
.imr_lshim
.shim_label
= dplane_ctx_get_pw_local_label(ctx
);
404 imr
.imr_rshim
.shim_label
= dplane_ctx_get_pw_remote_label(ctx
);
407 memset(&ifr
, 0, sizeof(ifr
));
408 strlcpy(ifr
.ifr_name
, dplane_ctx_get_ifname(ctx
),
409 sizeof(ifr
.ifr_name
));
410 ifr
.ifr_data
= (caddr_t
)&imr
;
411 if (ioctl(kr_state
.ioctl_fd
, SIOCSETMPWCFG
, &ifr
) == -1) {
412 flog_err_sys(EC_LIB_SYSTEM_CALL
, "ioctl SIOCSETMPWCFG: %s",
413 safe_strerror(errno
));
414 return ZEBRA_DPLANE_REQUEST_FAILURE
;
417 return ZEBRA_DPLANE_REQUEST_SUCCESS
;
420 static enum zebra_dplane_result
kmpw_uninstall(struct zebra_dplane_ctx
*ctx
)
425 memset(&ifr
, 0, sizeof(ifr
));
426 memset(&imr
, 0, sizeof(imr
));
427 strlcpy(ifr
.ifr_name
, dplane_ctx_get_ifname(ctx
),
428 sizeof(ifr
.ifr_name
));
429 ifr
.ifr_data
= (caddr_t
)&imr
;
430 if (ioctl(kr_state
.ioctl_fd
, SIOCSETMPWCFG
, &ifr
) == -1) {
431 flog_err_sys(EC_LIB_SYSTEM_CALL
, "ioctl SIOCSETMPWCFG: %s",
432 safe_strerror(errno
));
433 return ZEBRA_DPLANE_REQUEST_FAILURE
;
436 return ZEBRA_DPLANE_REQUEST_SUCCESS
;
440 * Pseudowire update api for openbsd.
442 enum zebra_dplane_result
kernel_pw_update(struct zebra_dplane_ctx
*ctx
)
444 enum zebra_dplane_result result
= ZEBRA_DPLANE_REQUEST_FAILURE
;
446 switch (dplane_ctx_get_op(ctx
)) {
447 case DPLANE_OP_PW_INSTALL
:
448 result
= kmpw_install(ctx
);
450 case DPLANE_OP_PW_UNINSTALL
:
451 result
= kmpw_uninstall(ctx
);
454 case DPLANE_OP_ROUTE_INSTALL
:
455 case DPLANE_OP_ROUTE_UPDATE
:
456 case DPLANE_OP_ROUTE_DELETE
:
457 case DPLANE_OP_ROUTE_NOTIFY
:
458 case DPLANE_OP_NH_INSTALL
:
459 case DPLANE_OP_NH_UPDATE
:
460 case DPLANE_OP_NH_DELETE
:
461 case DPLANE_OP_LSP_INSTALL
:
462 case DPLANE_OP_LSP_UPDATE
:
463 case DPLANE_OP_LSP_DELETE
:
464 case DPLANE_OP_LSP_NOTIFY
:
465 case DPLANE_OP_SYS_ROUTE_ADD
:
466 case DPLANE_OP_SYS_ROUTE_DELETE
:
467 case DPLANE_OP_ADDR_INSTALL
:
468 case DPLANE_OP_ADDR_UNINSTALL
:
469 case DPLANE_OP_MAC_INSTALL
:
470 case DPLANE_OP_MAC_DELETE
:
471 case DPLANE_OP_NEIGH_INSTALL
:
472 case DPLANE_OP_NEIGH_UPDATE
:
473 case DPLANE_OP_NEIGH_DELETE
:
474 case DPLANE_OP_VTEP_ADD
:
475 case DPLANE_OP_VTEP_DELETE
:
476 case DPLANE_OP_RULE_ADD
:
477 case DPLANE_OP_RULE_DELETE
:
478 case DPLANE_OP_RULE_UPDATE
:
479 case DPLANE_OP_NEIGH_DISCOVER
:
480 case DPLANE_OP_BR_PORT_UPDATE
:
481 case DPLANE_OP_IPTABLE_ADD
:
482 case DPLANE_OP_IPTABLE_DELETE
:
483 case DPLANE_OP_IPSET_ADD
:
484 case DPLANE_OP_IPSET_DELETE
:
485 case DPLANE_OP_IPSET_ENTRY_ADD
:
486 case DPLANE_OP_IPSET_ENTRY_DELETE
:
487 case DPLANE_OP_NEIGH_IP_INSTALL
:
488 case DPLANE_OP_NEIGH_IP_DELETE
:
489 case DPLANE_OP_NEIGH_TABLE_UPDATE
:
490 case DPLANE_OP_GRE_SET
:
491 case DPLANE_OP_INTF_ADDR_ADD
:
492 case DPLANE_OP_INTF_ADDR_DEL
:
493 case DPLANE_OP_INTF_NETCONFIG
:
494 case DPLANE_OP_INTF_INSTALL
:
495 case DPLANE_OP_INTF_UPDATE
:
496 case DPLANE_OP_INTF_DELETE
:
497 case DPLANE_OP_TC_QDISC_INSTALL
:
498 case DPLANE_OP_TC_QDISC_UNINSTALL
:
499 case DPLANE_OP_TC_CLASS_ADD
:
500 case DPLANE_OP_TC_CLASS_DELETE
:
501 case DPLANE_OP_TC_CLASS_UPDATE
:
502 case DPLANE_OP_TC_FILTER_ADD
:
503 case DPLANE_OP_TC_FILTER_DELETE
:
504 case DPLANE_OP_TC_FILTER_UPDATE
:
511 #define MAX_RTSOCK_BUF 128 * 1024
512 int mpls_kernel_init(void)
514 int rcvbuf
, default_rcvbuf
;
517 if ((kr_state
.fd
= socket(AF_ROUTE
, SOCK_RAW
, 0)) == -1) {
518 flog_err_sys(EC_LIB_SOCKET
, "%s: socket", __func__
);
522 if ((kr_state
.ioctl_fd
= socket(AF_INET
, SOCK_DGRAM
| SOCK_NONBLOCK
, 0))
524 flog_err_sys(EC_LIB_SOCKET
, "%s: ioctl socket", __func__
);
528 /* grow receive buffer, don't wanna miss messages */
529 optlen
= sizeof(default_rcvbuf
);
530 if (getsockopt(kr_state
.fd
, SOL_SOCKET
, SO_RCVBUF
, &default_rcvbuf
,
533 flog_err_sys(EC_LIB_SOCKET
,
534 "kr_init getsockopt SOL_SOCKET SO_RCVBUF");
536 for (rcvbuf
= MAX_RTSOCK_BUF
;
537 rcvbuf
> default_rcvbuf
538 && setsockopt(kr_state
.fd
, SOL_SOCKET
, SO_RCVBUF
, &rcvbuf
,
547 /* Strict pseudowire reachability checking required for obsd */
548 mpls_pw_reach_strict
= true;
553 #endif /* OPEN_BSD */