]>
Commit | Line | Data |
---|---|---|
942bf97b | 1 | /* |
2 | * Zebra Policy Based Routing (PBR) interaction with the kernel using | |
3 | * netlink. | |
4 | * Copyright (C) 2018 Cumulus Networks, Inc. | |
5 | * | |
6 | * This file is part of FRR. | |
7 | * | |
8 | * FRR is free software; you can redistribute it and/or modify it | |
9 | * under the terms of the GNU General Public License as published by the | |
10 | * Free Software Foundation; either version 2, or (at your option) any | |
11 | * later version. | |
12 | * | |
13 | * FRR is distributed in the hope that it will be useful, but | |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | * General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with FRR; see the file COPYING. If not, write to the Free | |
20 | * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |
21 | * 02111-1307, USA. | |
22 | */ | |
23 | ||
24 | #include <zebra.h> | |
25 | ||
26 | #ifdef HAVE_NETLINK | |
27 | ||
28 | #include "if.h" | |
29 | #include "prefix.h" | |
30 | #include "vrf.h" | |
31 | ||
32 | #include <linux/fib_rules.h> | |
33 | #include "zebra/zserv.h" | |
34 | #include "zebra/zebra_ns.h" | |
35 | #include "zebra/zebra_vrf.h" | |
36 | #include "zebra/rt.h" | |
37 | #include "zebra/interface.h" | |
38 | #include "zebra/debug.h" | |
39 | #include "zebra/rtadv.h" | |
40 | #include "zebra/kernel_netlink.h" | |
41 | #include "zebra/rule_netlink.h" | |
42 | #include "zebra/zebra_pbr.h" | |
9df414fe | 43 | #include "zebra/zebra_errors.h" |
942bf97b | 44 | |
45 | /* definitions */ | |
46 | ||
47 | /* static function declarations */ | |
48 | ||
49 | /* Private functions */ | |
50 | ||
51 | /* Install or uninstall specified rule for a specific interface. | |
52 | * Form netlink message and ship it. Currently, notify status after | |
53 | * waiting for netlink status. | |
54 | */ | |
a0321978 | 55 | static int netlink_rule_update(int cmd, struct zebra_pbr_rule *rule) |
942bf97b | 56 | { |
f3dbec60 | 57 | uint8_t protocol = RTPROT_ZEBRA; |
942bf97b | 58 | int family; |
59 | int bytelen; | |
60 | struct { | |
61 | struct nlmsghdr n; | |
62 | struct fib_rule_hdr frh; | |
63 | char buf[NL_PKT_BUF_SIZE]; | |
64 | } req; | |
65 | struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); | |
66 | struct sockaddr_nl snl; | |
67 | char buf1[PREFIX_STRLEN]; | |
68 | char buf2[PREFIX_STRLEN]; | |
69 | ||
fd71d73e | 70 | memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE); |
5dd0722d | 71 | family = PREFIX_FAMILY(&rule->rule.filter.src_ip); |
942bf97b | 72 | bytelen = (family == AF_INET ? 4 : 16); |
73 | ||
74 | req.n.nlmsg_type = cmd; | |
75 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); | |
76 | req.n.nlmsg_flags = NLM_F_REQUEST; | |
77 | req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid; | |
78 | ||
79 | req.frh.family = family; | |
80 | req.frh.action = FR_ACT_TO_TBL; | |
81 | ||
f3dbec60 DS |
82 | addattr_l(&req.n, sizeof(req), |
83 | FRA_PROTOCOL, &protocol, sizeof(protocol)); | |
84 | ||
942bf97b | 85 | /* rule's pref # */ |
5dd0722d | 86 | addattr32(&req.n, sizeof(req), FRA_PRIORITY, rule->rule.priority); |
942bf97b | 87 | |
88 | /* interface on which applied */ | |
a0321978 DS |
89 | if (rule->ifp) |
90 | addattr_l(&req.n, sizeof(req), FRA_IFNAME, rule->ifp->name, | |
91 | strlen(rule->ifp->name) + 1); | |
942bf97b | 92 | |
93 | /* source IP, if specified */ | |
94 | if (IS_RULE_FILTERING_ON_SRC_IP(rule)) { | |
5dd0722d | 95 | req.frh.src_len = rule->rule.filter.src_ip.prefixlen; |
942bf97b | 96 | addattr_l(&req.n, sizeof(req), FRA_SRC, |
5dd0722d | 97 | &rule->rule.filter.src_ip.u.prefix, bytelen); |
942bf97b | 98 | } |
99 | /* destination IP, if specified */ | |
100 | if (IS_RULE_FILTERING_ON_DST_IP(rule)) { | |
5dd0722d | 101 | req.frh.dst_len = rule->rule.filter.dst_ip.prefixlen; |
942bf97b | 102 | addattr_l(&req.n, sizeof(req), FRA_DST, |
5dd0722d | 103 | &rule->rule.filter.dst_ip.u.prefix, bytelen); |
942bf97b | 104 | } |
105 | ||
2bee7aae PG |
106 | /* fwmark, if specified */ |
107 | if (IS_RULE_FILTERING_ON_FWMARK(rule)) { | |
108 | addattr32(&req.n, sizeof(req), FRA_FWMARK, | |
109 | rule->rule.filter.fwmark); | |
110 | } | |
111 | ||
942bf97b | 112 | /* Route table to use to forward, if filter criteria matches. */ |
5dd0722d PG |
113 | if (rule->rule.action.table < 256) |
114 | req.frh.table = rule->rule.action.table; | |
942bf97b | 115 | else { |
116 | req.frh.table = RT_TABLE_UNSPEC; | |
117 | addattr32(&req.n, sizeof(req), FRA_TABLE, | |
5dd0722d | 118 | rule->rule.action.table); |
942bf97b | 119 | } |
120 | ||
121 | if (IS_ZEBRA_DEBUG_KERNEL) | |
fd71d73e | 122 | zlog_debug( |
15e6eed4 | 123 | "Tx %s family %s IF %s(%u) Pref %u Fwmark %u Src %s Dst %s Table %u", |
fd71d73e | 124 | nl_msg_type_to_str(cmd), nl_family_to_str(family), |
a0321978 | 125 | rule->ifp ? rule->ifp->name : "Unknown", |
5dd0722d | 126 | rule->ifp ? rule->ifp->ifindex : 0, rule->rule.priority, |
15e6eed4 | 127 | rule->rule.filter.fwmark, |
5dd0722d PG |
128 | prefix2str(&rule->rule.filter.src_ip, buf1, |
129 | sizeof(buf1)), | |
130 | prefix2str(&rule->rule.filter.dst_ip, buf2, | |
131 | sizeof(buf2)), | |
132 | rule->rule.action.table); | |
942bf97b | 133 | |
134 | /* Ship off the message. | |
135 | * Note: Currently, netlink_talk() is a blocking call which returns | |
136 | * back the status. | |
137 | */ | |
138 | memset(&snl, 0, sizeof(snl)); | |
139 | snl.nl_family = AF_NETLINK; | |
140 | return netlink_talk(netlink_talk_filter, &req.n, | |
141 | &zns->netlink_cmd, zns, 0); | |
142 | } | |
143 | ||
144 | ||
145 | /* Public functions */ | |
146 | /* | |
147 | * Install specified rule for a specific interface. The preference is what | |
148 | * goes in the rule to denote relative ordering; it may or may not be the | |
149 | * same as the rule's user-defined sequence number. | |
150 | */ | |
ea1c14f6 | 151 | enum zebra_dplane_result kernel_add_pbr_rule(struct zebra_pbr_rule *rule) |
942bf97b | 152 | { |
153 | int ret = 0; | |
154 | ||
a0321978 DS |
155 | ret = netlink_rule_update(RTM_NEWRULE, rule); |
156 | kernel_pbr_rule_add_del_status(rule, | |
ea1c14f6 MS |
157 | (!ret) ? ZEBRA_DPLANE_INSTALL_SUCCESS |
158 | : ZEBRA_DPLANE_INSTALL_FAILURE); | |
ebecd649 | 159 | |
ea1c14f6 | 160 | return ZEBRA_DPLANE_REQUEST_SUCCESS; |
942bf97b | 161 | } |
162 | ||
163 | /* | |
164 | * Uninstall specified rule for a specific interface. | |
165 | */ | |
ea1c14f6 | 166 | enum zebra_dplane_result kernel_del_pbr_rule(struct zebra_pbr_rule *rule) |
942bf97b | 167 | { |
168 | int ret = 0; | |
169 | ||
a0321978 DS |
170 | ret = netlink_rule_update(RTM_DELRULE, rule); |
171 | kernel_pbr_rule_add_del_status(rule, | |
ea1c14f6 MS |
172 | (!ret) ? ZEBRA_DPLANE_DELETE_SUCCESS |
173 | : ZEBRA_DPLANE_DELETE_FAILURE); | |
ebecd649 | 174 | |
ea1c14f6 | 175 | return ZEBRA_DPLANE_REQUEST_SUCCESS; |
942bf97b | 176 | } |
177 | ||
178 | /* | |
179 | * Handle netlink notification informing a rule add or delete. | |
180 | * Handling of an ADD is TBD. | |
181 | * DELs are notified up, if other attributes indicate it may be a | |
182 | * notification of interest. The expectation is that if this corresponds | |
183 | * to a PBR rule added by FRR, it will be readded. | |
184 | */ | |
2414abd3 | 185 | int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) |
942bf97b | 186 | { |
187 | struct zebra_ns *zns; | |
188 | struct fib_rule_hdr *frh; | |
189 | struct rtattr *tb[FRA_MAX + 1]; | |
190 | int len; | |
191 | char *ifname; | |
cc42104c | 192 | struct zebra_pbr_rule rule = {}; |
942bf97b | 193 | char buf1[PREFIX_STRLEN]; |
194 | char buf2[PREFIX_STRLEN]; | |
195 | ||
196 | /* Basic validation followed by extracting attributes. */ | |
197 | if (h->nlmsg_type != RTM_NEWRULE && h->nlmsg_type != RTM_DELRULE) | |
198 | return 0; | |
199 | ||
200 | /* TBD */ | |
201 | if (h->nlmsg_type == RTM_NEWRULE) | |
202 | return 0; | |
203 | ||
204 | len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct fib_rule_hdr)); | |
9bdf8618 DS |
205 | if (len < 0) { |
206 | zlog_err("%s: Message received from netlink is of a broken size: %d %zu", | |
207 | __PRETTY_FUNCTION__, h->nlmsg_len, | |
208 | (size_t)NLMSG_LENGTH(sizeof(struct fib_rule_hdr))); | |
942bf97b | 209 | return -1; |
9bdf8618 | 210 | } |
942bf97b | 211 | |
212 | frh = NLMSG_DATA(h); | |
8a1b681c | 213 | if (frh->family != AF_INET && frh->family != AF_INET6) { |
9df414fe | 214 | flog_warn( |
e914ccbe | 215 | EC_ZEBRA_NETLINK_INVALID_AF, |
81227874 | 216 | "Invalid address family: %u received from kernel rule change: %u", |
8a1b681c | 217 | frh->family, h->nlmsg_type); |
942bf97b | 218 | return 0; |
8a1b681c | 219 | } |
942bf97b | 220 | if (frh->action != FR_ACT_TO_TBL) |
221 | return 0; | |
222 | ||
223 | memset(tb, 0, sizeof(tb)); | |
224 | netlink_parse_rtattr(tb, FRA_MAX, RTM_RTA(frh), len); | |
225 | ||
226 | /* TBD: We don't care about rules not specifying an IIF. */ | |
227 | if (tb[FRA_IFNAME] == NULL) | |
228 | return 0; | |
229 | ||
230 | /* If we don't know the interface, we don't care. */ | |
231 | ifname = (char *)RTA_DATA(tb[FRA_IFNAME]); | |
232 | zns = zebra_ns_lookup(ns_id); | |
a0321978 DS |
233 | rule.ifp = if_lookup_by_name_per_ns(zns, ifname); |
234 | if (!rule.ifp) | |
942bf97b | 235 | return 0; |
236 | ||
942bf97b | 237 | if (tb[FRA_PRIORITY]) |
5dd0722d | 238 | rule.rule.priority = *(uint32_t *)RTA_DATA(tb[FRA_PRIORITY]); |
942bf97b | 239 | |
240 | if (tb[FRA_SRC]) { | |
241 | if (frh->family == AF_INET) | |
5dd0722d | 242 | memcpy(&rule.rule.filter.src_ip.u.prefix4, |
942bf97b | 243 | RTA_DATA(tb[FRA_SRC]), 4); |
244 | else | |
5dd0722d | 245 | memcpy(&rule.rule.filter.src_ip.u.prefix6, |
942bf97b | 246 | RTA_DATA(tb[FRA_SRC]), 16); |
5dd0722d PG |
247 | rule.rule.filter.src_ip.prefixlen = frh->src_len; |
248 | rule.rule.filter.filter_bm |= PBR_FILTER_SRC_IP; | |
942bf97b | 249 | } |
250 | ||
251 | if (tb[FRA_DST]) { | |
252 | if (frh->family == AF_INET) | |
5dd0722d | 253 | memcpy(&rule.rule.filter.dst_ip.u.prefix4, |
942bf97b | 254 | RTA_DATA(tb[FRA_DST]), 4); |
255 | else | |
5dd0722d | 256 | memcpy(&rule.rule.filter.dst_ip.u.prefix6, |
942bf97b | 257 | RTA_DATA(tb[FRA_DST]), 16); |
5dd0722d PG |
258 | rule.rule.filter.dst_ip.prefixlen = frh->dst_len; |
259 | rule.rule.filter.filter_bm |= PBR_FILTER_DST_IP; | |
942bf97b | 260 | } |
261 | ||
262 | if (tb[FRA_TABLE]) | |
5dd0722d | 263 | rule.rule.action.table = *(uint32_t *)RTA_DATA(tb[FRA_TABLE]); |
942bf97b | 264 | else |
5dd0722d | 265 | rule.rule.action.table = frh->table; |
942bf97b | 266 | |
267 | if (IS_ZEBRA_DEBUG_KERNEL) | |
fd71d73e DS |
268 | zlog_debug( |
269 | "Rx %s family %s IF %s(%u) Pref %u Src %s Dst %s Table %u", | |
270 | nl_msg_type_to_str(h->nlmsg_type), | |
a0321978 | 271 | nl_family_to_str(frh->family), rule.ifp->name, |
5dd0722d PG |
272 | rule.ifp->ifindex, rule.rule.priority, |
273 | prefix2str(&rule.rule.filter.src_ip, buf1, | |
274 | sizeof(buf1)), | |
275 | prefix2str(&rule.rule.filter.dst_ip, buf2, | |
276 | sizeof(buf2)), | |
277 | rule.rule.action.table); | |
fd71d73e | 278 | |
a0321978 | 279 | return kernel_pbr_rule_del(&rule); |
942bf97b | 280 | } |
281 | ||
282 | /* | |
283 | * Get to know existing PBR rules in the kernel - typically called at startup. | |
284 | * TBD. | |
285 | */ | |
286 | int netlink_rules_read(struct zebra_ns *zns) | |
287 | { | |
288 | return 0; | |
289 | } | |
290 | ||
291 | #endif /* HAVE_NETLINK */ |