2 * Copyright (C) 2016 by Open Source Routing.
4 * This file is part of GNU Zebra.
6 * GNU Zebra is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2, or (at your option) any
11 * GNU Zebra is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; see the file COPYING; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 #include <netmpls/mpls.h>
27 #include "zebra/zebra_mpls.h"
28 #include "zebra/debug.h"
29 #include "zebra/zebra_errors.h"
30 #include "zebra/zebra_router.h"
34 #include "interface.h"
36 #include "lib_errors.h"
38 extern struct zebra_privs_t zserv_privs
;
46 static int kernel_send_rtmsg_v4(int action
, mpls_label_t in_label
,
47 const struct zebra_nhlfe
*nhlfe
)
51 struct sockaddr_mpls sa_label_in
, sa_label_out
;
52 struct sockaddr_in nexthop
;
56 if (IS_ZEBRA_DEBUG_KERNEL
)
57 zlog_debug("%s: 0x%x, label=%u", __func__
, action
, in_label
);
59 /* initialize header */
60 memset(&hdr
, 0, sizeof(hdr
));
61 hdr
.rtm_version
= RTM_VERSION
;
63 hdr
.rtm_type
= action
;
64 hdr
.rtm_flags
= RTF_UP
;
65 hdr
.rtm_fmask
= RTF_MPLS
;
66 hdr
.rtm_seq
= kr_state
.rtseq
++; /* overflow doesn't matter */
67 hdr
.rtm_msglen
= sizeof(hdr
);
68 hdr
.rtm_hdrlen
= sizeof(struct rt_msghdr
);
71 iov
[iovcnt
].iov_base
= &hdr
;
72 iov
[iovcnt
++].iov_len
= sizeof(hdr
);
75 memset(&sa_label_in
, 0, sizeof(sa_label_in
));
76 sa_label_in
.smpls_len
= sizeof(sa_label_in
);
77 sa_label_in
.smpls_family
= AF_MPLS
;
78 sa_label_in
.smpls_label
= htonl(in_label
<< MPLS_LABEL_OFFSET
);
80 hdr
.rtm_flags
|= RTF_MPLS
| RTF_MPATH
;
81 hdr
.rtm_addrs
|= RTA_DST
;
82 hdr
.rtm_msglen
+= sizeof(sa_label_in
);
84 iov
[iovcnt
].iov_base
= &sa_label_in
;
85 iov
[iovcnt
++].iov_len
= sizeof(sa_label_in
);
88 memset(&nexthop
, 0, sizeof(nexthop
));
89 nexthop
.sin_len
= sizeof(nexthop
);
90 nexthop
.sin_family
= AF_INET
;
91 nexthop
.sin_addr
= nhlfe
->nexthop
->gate
.ipv4
;
93 hdr
.rtm_flags
|= RTF_GATEWAY
;
94 hdr
.rtm_addrs
|= RTA_GATEWAY
;
95 hdr
.rtm_msglen
+= sizeof(nexthop
);
97 iov
[iovcnt
].iov_base
= &nexthop
;
98 iov
[iovcnt
++].iov_len
= sizeof(nexthop
);
100 /* If action is RTM_DELETE we have to get rid of MPLS infos */
101 if (action
!= RTM_DELETE
) {
102 memset(&sa_label_out
, 0, sizeof(sa_label_out
));
103 sa_label_out
.smpls_len
= sizeof(sa_label_out
);
104 sa_label_out
.smpls_family
= AF_MPLS
;
105 sa_label_out
.smpls_label
=
106 htonl(nhlfe
->nexthop
->nh_label
->label
[0]
107 << MPLS_LABEL_OFFSET
);
109 hdr
.rtm_addrs
|= RTA_SRC
;
110 hdr
.rtm_flags
|= RTF_MPLS
;
111 hdr
.rtm_msglen
+= sizeof(sa_label_out
);
113 iov
[iovcnt
].iov_base
= &sa_label_out
;
114 iov
[iovcnt
++].iov_len
= sizeof(sa_label_out
);
116 if (nhlfe
->nexthop
->nh_label
->label
[0] == MPLS_LABEL_IMPLNULL
)
117 hdr
.rtm_mpls
= MPLS_OP_POP
;
119 hdr
.rtm_mpls
= MPLS_OP_SWAP
;
122 frr_with_privs(&zserv_privs
) {
123 ret
= writev(kr_state
.fd
, iov
, iovcnt
);
127 flog_err_sys(EC_LIB_SOCKET
, "%s: %s", __func__
,
128 safe_strerror(errno
));
133 #if !defined(ROUNDUP)
135 (((a) & (sizeof(long) - 1)) ? (1 + ((a) | (sizeof(long) - 1))) : (a))
138 static int kernel_send_rtmsg_v6(int action
, mpls_label_t in_label
,
139 const struct zebra_nhlfe
*nhlfe
)
142 struct rt_msghdr hdr
;
143 struct sockaddr_mpls sa_label_in
, sa_label_out
;
145 struct sockaddr_in6 addr
;
146 char pad
[sizeof(long)]; /* thank you IPv6 */
151 if (IS_ZEBRA_DEBUG_KERNEL
)
152 zlog_debug("%s: 0x%x, label=%u", __func__
, action
, in_label
);
154 /* initialize header */
155 memset(&hdr
, 0, sizeof(hdr
));
156 hdr
.rtm_version
= RTM_VERSION
;
158 hdr
.rtm_type
= action
;
159 hdr
.rtm_flags
= RTF_UP
;
160 hdr
.rtm_fmask
= RTF_MPLS
;
161 hdr
.rtm_seq
= kr_state
.rtseq
++; /* overflow doesn't matter */
162 hdr
.rtm_msglen
= sizeof(hdr
);
163 hdr
.rtm_hdrlen
= sizeof(struct rt_msghdr
);
164 hdr
.rtm_priority
= 0;
166 iov
[iovcnt
].iov_base
= &hdr
;
167 iov
[iovcnt
++].iov_len
= sizeof(hdr
);
170 memset(&sa_label_in
, 0, sizeof(sa_label_in
));
171 sa_label_in
.smpls_len
= sizeof(sa_label_in
);
172 sa_label_in
.smpls_family
= AF_MPLS
;
173 sa_label_in
.smpls_label
= htonl(in_label
<< MPLS_LABEL_OFFSET
);
175 hdr
.rtm_flags
|= RTF_MPLS
| RTF_MPATH
;
176 hdr
.rtm_addrs
|= RTA_DST
;
177 hdr
.rtm_msglen
+= sizeof(sa_label_in
);
179 iov
[iovcnt
].iov_base
= &sa_label_in
;
180 iov
[iovcnt
++].iov_len
= sizeof(sa_label_in
);
183 memset(&nexthop
, 0, sizeof(nexthop
));
184 nexthop
.addr
.sin6_len
= sizeof(struct sockaddr_in6
);
185 nexthop
.addr
.sin6_family
= AF_INET6
;
186 nexthop
.addr
.sin6_addr
= nhlfe
->nexthop
->gate
.ipv6
;
187 if (IN6_IS_ADDR_LINKLOCAL(&nexthop
.addr
.sin6_addr
)) {
189 struct sockaddr_in6
*sin6
= &nexthop
.addr
;
191 nexthop
.addr
.sin6_scope_id
= nhlfe
->nexthop
->ifindex
;
193 memcpy(&tmp16
, &sin6
->sin6_addr
.s6_addr
[2], sizeof(tmp16
));
194 tmp16
= htons(sin6
->sin6_scope_id
);
195 memcpy(&sin6
->sin6_addr
.s6_addr
[2], &tmp16
, sizeof(tmp16
));
196 sin6
->sin6_scope_id
= 0;
200 hdr
.rtm_flags
|= RTF_GATEWAY
;
201 hdr
.rtm_addrs
|= RTA_GATEWAY
;
202 hdr
.rtm_msglen
+= ROUNDUP(sizeof(struct sockaddr_in6
));
204 iov
[iovcnt
].iov_base
= &nexthop
;
205 iov
[iovcnt
++].iov_len
= ROUNDUP(sizeof(struct sockaddr_in6
));
207 /* If action is RTM_DELETE we have to get rid of MPLS infos */
208 if (action
!= RTM_DELETE
) {
209 memset(&sa_label_out
, 0, sizeof(sa_label_out
));
210 sa_label_out
.smpls_len
= sizeof(sa_label_out
);
211 sa_label_out
.smpls_family
= AF_MPLS
;
212 sa_label_out
.smpls_label
=
213 htonl(nhlfe
->nexthop
->nh_label
->label
[0]
214 << MPLS_LABEL_OFFSET
);
216 hdr
.rtm_addrs
|= RTA_SRC
;
217 hdr
.rtm_flags
|= RTF_MPLS
;
218 hdr
.rtm_msglen
+= sizeof(sa_label_out
);
220 iov
[iovcnt
].iov_base
= &sa_label_out
;
221 iov
[iovcnt
++].iov_len
= sizeof(sa_label_out
);
223 if (nhlfe
->nexthop
->nh_label
->label
[0] == MPLS_LABEL_IMPLNULL
)
224 hdr
.rtm_mpls
= MPLS_OP_POP
;
226 hdr
.rtm_mpls
= MPLS_OP_SWAP
;
229 frr_with_privs(&zserv_privs
) {
230 ret
= writev(kr_state
.fd
, iov
, iovcnt
);
234 flog_err_sys(EC_LIB_SOCKET
, "%s: %s", __func__
,
235 safe_strerror(errno
));
240 static int kernel_lsp_cmd(struct zebra_dplane_ctx
*ctx
)
242 const struct nhlfe_list_head
*head
;
243 const struct zebra_nhlfe
*nhlfe
;
244 const struct nexthop
*nexthop
= NULL
;
245 unsigned int nexthop_num
= 0;
248 switch (dplane_ctx_get_op(ctx
)) {
249 case DPLANE_OP_LSP_DELETE
:
252 case DPLANE_OP_LSP_INSTALL
:
255 case DPLANE_OP_LSP_UPDATE
:
262 head
= dplane_ctx_get_nhlfe_list(ctx
);
263 frr_each(nhlfe_list_const
, head
, nhlfe
) {
264 nexthop
= nhlfe
->nexthop
;
268 if (nexthop_num
>= zrouter
.multipath_num
)
271 if (((action
== RTM_ADD
|| action
== RTM_CHANGE
)
272 && (CHECK_FLAG(nhlfe
->flags
, NHLFE_FLAG_SELECTED
)
273 && CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
)))
274 || (action
== RTM_DELETE
275 && (CHECK_FLAG(nhlfe
->flags
, NHLFE_FLAG_INSTALLED
)
276 && CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_FIB
)))) {
277 if (nhlfe
->nexthop
->nh_label
->num_labels
> 1) {
278 flog_warn(EC_ZEBRA_MAX_LABELS_PUSH
,
279 "%s: can't push %u labels at once (maximum is 1)",
281 nhlfe
->nexthop
->nh_label
->num_labels
);
287 switch (NHLFE_FAMILY(nhlfe
)) {
289 kernel_send_rtmsg_v4(
291 dplane_ctx_get_in_label(ctx
),
295 kernel_send_rtmsg_v6(
297 dplane_ctx_get_in_label(ctx
),
309 enum zebra_dplane_result
kernel_lsp_update(struct zebra_dplane_ctx
*ctx
)
313 ret
= kernel_lsp_cmd(ctx
);
316 ZEBRA_DPLANE_REQUEST_SUCCESS
: ZEBRA_DPLANE_REQUEST_FAILURE
);
319 static enum zebra_dplane_result
kmpw_install(struct zebra_dplane_ctx
*ctx
)
323 struct sockaddr_storage ss
;
324 struct sockaddr_in
*sa_in
= (struct sockaddr_in
*)&ss
;
325 struct sockaddr_in6
*sa_in6
= (struct sockaddr_in6
*)&ss
;
326 const union g_addr
*gaddr
;
328 memset(&imr
, 0, sizeof(imr
));
329 switch (dplane_ctx_get_pw_type(ctx
)) {
330 case PW_TYPE_ETHERNET
:
331 imr
.imr_type
= IMR_TYPE_ETHERNET
;
333 case PW_TYPE_ETHERNET_TAGGED
:
334 imr
.imr_type
= IMR_TYPE_ETHERNET_TAGGED
;
337 zlog_debug("%s: unhandled pseudowire type (%#X)", __func__
,
338 dplane_ctx_get_pw_type(ctx
));
339 return ZEBRA_DPLANE_REQUEST_FAILURE
;
342 if (dplane_ctx_get_pw_flags(ctx
) & F_PSEUDOWIRE_CWORD
)
343 imr
.imr_flags
|= IMR_FLAG_CONTROLWORD
;
345 /* pseudowire nexthop */
346 memset(&ss
, 0, sizeof(ss
));
347 gaddr
= dplane_ctx_get_pw_dest(ctx
);
348 switch (dplane_ctx_get_pw_af(ctx
)) {
350 sa_in
->sin_family
= AF_INET
;
351 sa_in
->sin_len
= sizeof(struct sockaddr_in
);
352 sa_in
->sin_addr
= gaddr
->ipv4
;
355 sa_in6
->sin6_family
= AF_INET6
;
356 sa_in6
->sin6_len
= sizeof(struct sockaddr_in6
);
357 sa_in6
->sin6_addr
= gaddr
->ipv6
;
360 zlog_debug("%s: unhandled pseudowire address-family (%u)",
361 __func__
, dplane_ctx_get_pw_af(ctx
));
362 return ZEBRA_DPLANE_REQUEST_FAILURE
;
364 memcpy(&imr
.imr_nexthop
, (struct sockaddr
*)&ss
,
365 sizeof(imr
.imr_nexthop
));
367 /* pseudowire local/remote labels */
368 imr
.imr_lshim
.shim_label
= dplane_ctx_get_pw_local_label(ctx
);
369 imr
.imr_rshim
.shim_label
= dplane_ctx_get_pw_remote_label(ctx
);
372 memset(&ifr
, 0, sizeof(ifr
));
373 strlcpy(ifr
.ifr_name
, dplane_ctx_get_ifname(ctx
),
374 sizeof(ifr
.ifr_name
));
375 ifr
.ifr_data
= (caddr_t
)&imr
;
376 if (ioctl(kr_state
.ioctl_fd
, SIOCSETMPWCFG
, &ifr
) == -1) {
377 flog_err_sys(EC_LIB_SYSTEM_CALL
, "ioctl SIOCSETMPWCFG: %s",
378 safe_strerror(errno
));
379 return ZEBRA_DPLANE_REQUEST_FAILURE
;
382 return ZEBRA_DPLANE_REQUEST_SUCCESS
;
385 static enum zebra_dplane_result
kmpw_uninstall(struct zebra_dplane_ctx
*ctx
)
390 memset(&ifr
, 0, sizeof(ifr
));
391 memset(&imr
, 0, sizeof(imr
));
392 strlcpy(ifr
.ifr_name
, dplane_ctx_get_ifname(ctx
),
393 sizeof(ifr
.ifr_name
));
394 ifr
.ifr_data
= (caddr_t
)&imr
;
395 if (ioctl(kr_state
.ioctl_fd
, SIOCSETMPWCFG
, &ifr
) == -1) {
396 flog_err_sys(EC_LIB_SYSTEM_CALL
, "ioctl SIOCSETMPWCFG: %s",
397 safe_strerror(errno
));
398 return ZEBRA_DPLANE_REQUEST_FAILURE
;
401 return ZEBRA_DPLANE_REQUEST_SUCCESS
;
405 * Pseudowire update api for openbsd.
407 enum zebra_dplane_result
kernel_pw_update(struct zebra_dplane_ctx
*ctx
)
409 enum zebra_dplane_result result
= ZEBRA_DPLANE_REQUEST_FAILURE
;
411 switch (dplane_ctx_get_op(ctx
)) {
412 case DPLANE_OP_PW_INSTALL
:
413 result
= kmpw_install(ctx
);
415 case DPLANE_OP_PW_UNINSTALL
:
416 result
= kmpw_uninstall(ctx
);
425 #define MAX_RTSOCK_BUF 128 * 1024
426 int mpls_kernel_init(void)
428 int rcvbuf
, default_rcvbuf
;
431 if ((kr_state
.fd
= socket(AF_ROUTE
, SOCK_RAW
, 0)) == -1) {
432 flog_err_sys(EC_LIB_SOCKET
, "%s: socket", __func__
);
436 if ((kr_state
.ioctl_fd
= socket(AF_INET
, SOCK_DGRAM
| SOCK_NONBLOCK
, 0))
438 flog_err_sys(EC_LIB_SOCKET
, "%s: ioctl socket", __func__
);
442 /* grow receive buffer, don't wanna miss messages */
443 optlen
= sizeof(default_rcvbuf
);
444 if (getsockopt(kr_state
.fd
, SOL_SOCKET
, SO_RCVBUF
, &default_rcvbuf
,
447 flog_err_sys(EC_LIB_SOCKET
,
448 "kr_init getsockopt SOL_SOCKET SO_RCVBUF");
450 for (rcvbuf
= MAX_RTSOCK_BUF
;
451 rcvbuf
> default_rcvbuf
452 && setsockopt(kr_state
.fd
, SOL_SOCKET
, SO_RCVBUF
, &rcvbuf
,
461 /* Strict pseudowire reachability checking required for obsd */
462 mpls_pw_reach_strict
= true;
467 #endif /* OPEN_BSD */