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"
32 #include "interface.h"
34 #include "lib_errors.h"
36 extern struct zebra_privs_t zserv_privs
;
44 static int kernel_send_rtmsg_v4(int action
, mpls_label_t in_label
,
49 struct sockaddr_mpls sa_label_in
, sa_label_out
;
50 struct sockaddr_in nexthop
;
54 if (IS_ZEBRA_DEBUG_KERNEL
)
55 zlog_debug("%s: 0x%x, label=%u", __func__
, action
, in_label
);
57 /* initialize header */
58 memset(&hdr
, 0, sizeof(hdr
));
59 hdr
.rtm_version
= RTM_VERSION
;
61 hdr
.rtm_type
= action
;
62 hdr
.rtm_flags
= RTF_UP
;
63 hdr
.rtm_fmask
= RTF_MPLS
;
64 hdr
.rtm_seq
= kr_state
.rtseq
++; /* overflow doesn't matter */
65 hdr
.rtm_msglen
= sizeof(hdr
);
66 hdr
.rtm_hdrlen
= sizeof(struct rt_msghdr
);
69 iov
[iovcnt
].iov_base
= &hdr
;
70 iov
[iovcnt
++].iov_len
= sizeof(hdr
);
73 memset(&sa_label_in
, 0, sizeof(sa_label_in
));
74 sa_label_in
.smpls_len
= sizeof(sa_label_in
);
75 sa_label_in
.smpls_family
= AF_MPLS
;
76 sa_label_in
.smpls_label
= htonl(in_label
<< MPLS_LABEL_OFFSET
);
78 hdr
.rtm_flags
|= RTF_MPLS
| RTF_MPATH
;
79 hdr
.rtm_addrs
|= RTA_DST
;
80 hdr
.rtm_msglen
+= sizeof(sa_label_in
);
82 iov
[iovcnt
].iov_base
= &sa_label_in
;
83 iov
[iovcnt
++].iov_len
= sizeof(sa_label_in
);
86 memset(&nexthop
, 0, sizeof(nexthop
));
87 nexthop
.sin_len
= sizeof(nexthop
);
88 nexthop
.sin_family
= AF_INET
;
89 nexthop
.sin_addr
= nhlfe
->nexthop
->gate
.ipv4
;
91 hdr
.rtm_flags
|= RTF_GATEWAY
;
92 hdr
.rtm_addrs
|= RTA_GATEWAY
;
93 hdr
.rtm_msglen
+= sizeof(nexthop
);
95 iov
[iovcnt
].iov_base
= &nexthop
;
96 iov
[iovcnt
++].iov_len
= sizeof(nexthop
);
98 /* If action is RTM_DELETE we have to get rid of MPLS infos */
99 if (action
!= RTM_DELETE
) {
100 memset(&sa_label_out
, 0, sizeof(sa_label_out
));
101 sa_label_out
.smpls_len
= sizeof(sa_label_out
);
102 sa_label_out
.smpls_family
= AF_MPLS
;
103 sa_label_out
.smpls_label
=
104 htonl(nhlfe
->nexthop
->nh_label
->label
[0]
105 << MPLS_LABEL_OFFSET
);
107 hdr
.rtm_addrs
|= RTA_SRC
;
108 hdr
.rtm_flags
|= RTF_MPLS
;
109 hdr
.rtm_msglen
+= sizeof(sa_label_out
);
111 iov
[iovcnt
].iov_base
= &sa_label_out
;
112 iov
[iovcnt
++].iov_len
= sizeof(sa_label_out
);
114 if (nhlfe
->nexthop
->nh_label
->label
[0] == MPLS_LABEL_IMPLNULL
)
115 hdr
.rtm_mpls
= MPLS_OP_POP
;
117 hdr
.rtm_mpls
= MPLS_OP_SWAP
;
120 frr_elevate_privs(&zserv_privs
) {
121 ret
= writev(kr_state
.fd
, iov
, iovcnt
);
125 flog_err_sys(LIB_ERR_SOCKET
, "%s: %s", __func__
,
126 safe_strerror(errno
));
131 #if !defined(ROUNDUP)
133 (((a) & (sizeof(long) - 1)) ? (1 + ((a) | (sizeof(long) - 1))) : (a))
136 static int kernel_send_rtmsg_v6(int action
, mpls_label_t in_label
,
137 zebra_nhlfe_t
*nhlfe
)
140 struct rt_msghdr hdr
;
141 struct sockaddr_mpls sa_label_in
, sa_label_out
;
143 struct sockaddr_in6 addr
;
144 char pad
[sizeof(long)]; /* thank you IPv6 */
149 if (IS_ZEBRA_DEBUG_KERNEL
)
150 zlog_debug("%s: 0x%x, label=%u", __func__
, action
, in_label
);
152 /* initialize header */
153 memset(&hdr
, 0, sizeof(hdr
));
154 hdr
.rtm_version
= RTM_VERSION
;
156 hdr
.rtm_type
= action
;
157 hdr
.rtm_flags
= RTF_UP
;
158 hdr
.rtm_fmask
= RTF_MPLS
;
159 hdr
.rtm_seq
= kr_state
.rtseq
++; /* overflow doesn't matter */
160 hdr
.rtm_msglen
= sizeof(hdr
);
161 hdr
.rtm_hdrlen
= sizeof(struct rt_msghdr
);
162 hdr
.rtm_priority
= 0;
164 iov
[iovcnt
].iov_base
= &hdr
;
165 iov
[iovcnt
++].iov_len
= sizeof(hdr
);
168 memset(&sa_label_in
, 0, sizeof(sa_label_in
));
169 sa_label_in
.smpls_len
= sizeof(sa_label_in
);
170 sa_label_in
.smpls_family
= AF_MPLS
;
171 sa_label_in
.smpls_label
= htonl(in_label
<< MPLS_LABEL_OFFSET
);
173 hdr
.rtm_flags
|= RTF_MPLS
| RTF_MPATH
;
174 hdr
.rtm_addrs
|= RTA_DST
;
175 hdr
.rtm_msglen
+= sizeof(sa_label_in
);
177 iov
[iovcnt
].iov_base
= &sa_label_in
;
178 iov
[iovcnt
++].iov_len
= sizeof(sa_label_in
);
181 memset(&nexthop
, 0, sizeof(nexthop
));
182 nexthop
.addr
.sin6_len
= sizeof(struct sockaddr_in6
);
183 nexthop
.addr
.sin6_family
= AF_INET6
;
184 nexthop
.addr
.sin6_addr
= nhlfe
->nexthop
->gate
.ipv6
;
185 if (IN6_IS_ADDR_LINKLOCAL(&nexthop
.addr
.sin6_addr
)) {
187 struct sockaddr_in6
*sin6
= &nexthop
.addr
;
189 nexthop
.addr
.sin6_scope_id
= nhlfe
->nexthop
->ifindex
;
191 memcpy(&tmp16
, &sin6
->sin6_addr
.s6_addr
[2], sizeof(tmp16
));
192 tmp16
= htons(sin6
->sin6_scope_id
);
193 memcpy(&sin6
->sin6_addr
.s6_addr
[2], &tmp16
, sizeof(tmp16
));
194 sin6
->sin6_scope_id
= 0;
198 hdr
.rtm_flags
|= RTF_GATEWAY
;
199 hdr
.rtm_addrs
|= RTA_GATEWAY
;
200 hdr
.rtm_msglen
+= ROUNDUP(sizeof(struct sockaddr_in6
));
202 iov
[iovcnt
].iov_base
= &nexthop
;
203 iov
[iovcnt
++].iov_len
= ROUNDUP(sizeof(struct sockaddr_in6
));
205 /* If action is RTM_DELETE we have to get rid of MPLS infos */
206 if (action
!= RTM_DELETE
) {
207 memset(&sa_label_out
, 0, sizeof(sa_label_out
));
208 sa_label_out
.smpls_len
= sizeof(sa_label_out
);
209 sa_label_out
.smpls_family
= AF_MPLS
;
210 sa_label_out
.smpls_label
=
211 htonl(nhlfe
->nexthop
->nh_label
->label
[0]
212 << MPLS_LABEL_OFFSET
);
214 hdr
.rtm_addrs
|= RTA_SRC
;
215 hdr
.rtm_flags
|= RTF_MPLS
;
216 hdr
.rtm_msglen
+= sizeof(sa_label_out
);
218 iov
[iovcnt
].iov_base
= &sa_label_out
;
219 iov
[iovcnt
++].iov_len
= sizeof(sa_label_out
);
221 if (nhlfe
->nexthop
->nh_label
->label
[0] == MPLS_LABEL_IMPLNULL
)
222 hdr
.rtm_mpls
= MPLS_OP_POP
;
224 hdr
.rtm_mpls
= MPLS_OP_SWAP
;
227 frr_elevate_privs(&zserv_privs
) {
228 ret
= writev(kr_state
.fd
, iov
, iovcnt
);
232 flog_err_sys(LIB_ERR_SOCKET
, "%s: %s", __func__
,
233 safe_strerror(errno
));
238 static int kernel_lsp_cmd(int action
, zebra_lsp_t
*lsp
)
240 zebra_nhlfe_t
*nhlfe
;
241 struct nexthop
*nexthop
= NULL
;
242 unsigned int nexthop_num
= 0;
244 for (nhlfe
= lsp
->nhlfe_list
; nhlfe
; nhlfe
= nhlfe
->next
) {
245 nexthop
= nhlfe
->nexthop
;
249 if (nexthop_num
>= multipath_num
)
252 if (((action
== RTM_ADD
|| action
== RTM_CHANGE
)
253 && (CHECK_FLAG(nhlfe
->flags
, NHLFE_FLAG_SELECTED
)
254 && CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_ACTIVE
)))
255 || (action
== RTM_DELETE
256 && (CHECK_FLAG(nhlfe
->flags
, NHLFE_FLAG_INSTALLED
)
257 && CHECK_FLAG(nexthop
->flags
, NEXTHOP_FLAG_FIB
)))) {
258 if (nhlfe
->nexthop
->nh_label
->num_labels
> 1) {
260 "%s: can't push %u labels at once "
263 nhlfe
->nexthop
->nh_label
->num_labels
);
269 switch (NHLFE_FAMILY(nhlfe
)) {
271 kernel_send_rtmsg_v4(action
, lsp
->ile
.in_label
,
275 kernel_send_rtmsg_v6(action
, lsp
->ile
.in_label
,
287 enum dp_req_result
kernel_add_lsp(zebra_lsp_t
*lsp
)
291 if (!lsp
|| !lsp
->best_nhlfe
) { // unexpected
292 kernel_lsp_pass_fail(lsp
, DP_INSTALL_FAILURE
);
293 return DP_REQUEST_FAILURE
;
296 ret
= kernel_lsp_cmd(RTM_ADD
, lsp
);
298 kernel_lsp_pass_fail(lsp
,
299 (!ret
) ? DP_INSTALL_SUCCESS
300 : DP_INSTALL_FAILURE
);
302 return DP_REQUEST_SUCCESS
;
305 enum dp_req_result
kernel_upd_lsp(zebra_lsp_t
*lsp
)
309 if (!lsp
|| !lsp
->best_nhlfe
) { // unexpected
310 kernel_lsp_pass_fail(lsp
, DP_INSTALL_FAILURE
);
311 return DP_REQUEST_FAILURE
;
314 ret
= kernel_lsp_cmd(RTM_CHANGE
, lsp
);
316 kernel_lsp_pass_fail(lsp
,
317 (!ret
) ? DP_INSTALL_SUCCESS
318 : DP_INSTALL_FAILURE
);
319 return DP_REQUEST_SUCCESS
;
322 enum dp_req_result
kernel_del_lsp(zebra_lsp_t
*lsp
)
326 if (!lsp
) { // unexpected
327 kernel_lsp_pass_fail(lsp
, DP_DELETE_FAILURE
);
328 return DP_REQUEST_FAILURE
;
331 if (!CHECK_FLAG(lsp
->flags
, LSP_FLAG_INSTALLED
)) {
332 kernel_lsp_pass_fail(lsp
, DP_DELETE_FAILURE
);
333 return DP_REQUEST_FAILURE
;
336 ret
= kernel_lsp_cmd(RTM_DELETE
, lsp
);
338 kernel_lsp_pass_fail(lsp
,
339 (!ret
) ? DP_DELETE_SUCCESS
340 : DP_DELETE_FAILURE
);
342 return DP_REQUEST_SUCCESS
;
345 static int kmpw_install(struct zebra_pw
*pw
)
349 struct sockaddr_storage ss
;
350 struct sockaddr_in
*sa_in
= (struct sockaddr_in
*)&ss
;
351 struct sockaddr_in6
*sa_in6
= (struct sockaddr_in6
*)&ss
;
353 memset(&imr
, 0, sizeof(imr
));
355 case PW_TYPE_ETHERNET
:
356 imr
.imr_type
= IMR_TYPE_ETHERNET
;
358 case PW_TYPE_ETHERNET_TAGGED
:
359 imr
.imr_type
= IMR_TYPE_ETHERNET_TAGGED
;
362 zlog_warn("%s: unhandled pseudowire type (%#X)", __func__
,
367 if (pw
->flags
& F_PSEUDOWIRE_CWORD
)
368 imr
.imr_flags
|= IMR_FLAG_CONTROLWORD
;
370 /* pseudowire nexthop */
371 memset(&ss
, 0, sizeof(ss
));
374 sa_in
->sin_family
= AF_INET
;
375 sa_in
->sin_len
= sizeof(struct sockaddr_in
);
376 sa_in
->sin_addr
= pw
->nexthop
.ipv4
;
379 sa_in6
->sin6_family
= AF_INET6
;
380 sa_in6
->sin6_len
= sizeof(struct sockaddr_in6
);
381 sa_in6
->sin6_addr
= pw
->nexthop
.ipv6
;
384 zlog_warn("%s: unhandled pseudowire address-family (%u)",
388 memcpy(&imr
.imr_nexthop
, (struct sockaddr
*)&ss
,
389 sizeof(imr
.imr_nexthop
));
391 /* pseudowire local/remote labels */
392 imr
.imr_lshim
.shim_label
= pw
->local_label
;
393 imr
.imr_rshim
.shim_label
= pw
->remote_label
;
396 memset(&ifr
, 0, sizeof(ifr
));
397 strlcpy(ifr
.ifr_name
, pw
->ifname
, sizeof(ifr
.ifr_name
));
398 ifr
.ifr_data
= (caddr_t
)&imr
;
399 if (ioctl(kr_state
.ioctl_fd
, SIOCSETMPWCFG
, &ifr
) == -1) {
400 flog_err_sys(LIB_ERR_SYSTEM_CALL
, "ioctl SIOCSETMPWCFG: %s",
401 safe_strerror(errno
));
408 static int kmpw_uninstall(struct zebra_pw
*pw
)
413 memset(&ifr
, 0, sizeof(ifr
));
414 memset(&imr
, 0, sizeof(imr
));
415 strlcpy(ifr
.ifr_name
, pw
->ifname
, sizeof(ifr
.ifr_name
));
416 ifr
.ifr_data
= (caddr_t
)&imr
;
417 if (ioctl(kr_state
.ioctl_fd
, SIOCSETMPWCFG
, &ifr
) == -1) {
418 flog_err_sys(LIB_ERR_SYSTEM_CALL
, "ioctl SIOCSETMPWCFG: %s",
419 safe_strerror(errno
));
426 #define MAX_RTSOCK_BUF 128 * 1024
427 int mpls_kernel_init(void)
429 int rcvbuf
, default_rcvbuf
;
432 if ((kr_state
.fd
= socket(AF_ROUTE
, SOCK_RAW
, 0)) == -1) {
433 zlog_warn("%s: socket", __func__
);
437 if ((kr_state
.ioctl_fd
= socket(AF_INET
, SOCK_DGRAM
| SOCK_NONBLOCK
, 0))
439 zlog_warn("%s: ioctl socket", __func__
);
443 /* grow receive buffer, don't wanna miss messages */
444 optlen
= sizeof(default_rcvbuf
);
445 if (getsockopt(kr_state
.fd
, SOL_SOCKET
, SO_RCVBUF
, &default_rcvbuf
,
448 zlog_warn("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 /* register hook to install/uninstall pseudowires */
462 hook_register(pw_install
, kmpw_install
);
463 hook_register(pw_uninstall
, kmpw_uninstall
);
468 #endif /* OPEN_BSD */