]>
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 | { |
57 | int family; | |
58 | int bytelen; | |
59 | struct { | |
60 | struct nlmsghdr n; | |
61 | struct fib_rule_hdr frh; | |
62 | char buf[NL_PKT_BUF_SIZE]; | |
63 | } req; | |
64 | struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); | |
65 | struct sockaddr_nl snl; | |
66 | char buf1[PREFIX_STRLEN]; | |
67 | char buf2[PREFIX_STRLEN]; | |
68 | ||
fd71d73e | 69 | memset(&req, 0, sizeof(req) - NL_PKT_BUF_SIZE); |
5dd0722d | 70 | family = PREFIX_FAMILY(&rule->rule.filter.src_ip); |
942bf97b | 71 | bytelen = (family == AF_INET ? 4 : 16); |
72 | ||
73 | req.n.nlmsg_type = cmd; | |
74 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); | |
75 | req.n.nlmsg_flags = NLM_F_REQUEST; | |
76 | req.n.nlmsg_pid = zns->netlink_cmd.snl.nl_pid; | |
77 | ||
78 | req.frh.family = family; | |
79 | req.frh.action = FR_ACT_TO_TBL; | |
80 | ||
942bf97b | 81 | /* rule's pref # */ |
5dd0722d | 82 | addattr32(&req.n, sizeof(req), FRA_PRIORITY, rule->rule.priority); |
942bf97b | 83 | |
84 | /* interface on which applied */ | |
a0321978 DS |
85 | if (rule->ifp) |
86 | addattr_l(&req.n, sizeof(req), FRA_IFNAME, rule->ifp->name, | |
87 | strlen(rule->ifp->name) + 1); | |
942bf97b | 88 | |
89 | /* source IP, if specified */ | |
90 | if (IS_RULE_FILTERING_ON_SRC_IP(rule)) { | |
5dd0722d | 91 | req.frh.src_len = rule->rule.filter.src_ip.prefixlen; |
942bf97b | 92 | addattr_l(&req.n, sizeof(req), FRA_SRC, |
5dd0722d | 93 | &rule->rule.filter.src_ip.u.prefix, bytelen); |
942bf97b | 94 | } |
95 | /* destination IP, if specified */ | |
96 | if (IS_RULE_FILTERING_ON_DST_IP(rule)) { | |
5dd0722d | 97 | req.frh.dst_len = rule->rule.filter.dst_ip.prefixlen; |
942bf97b | 98 | addattr_l(&req.n, sizeof(req), FRA_DST, |
5dd0722d | 99 | &rule->rule.filter.dst_ip.u.prefix, bytelen); |
942bf97b | 100 | } |
101 | ||
2bee7aae PG |
102 | /* fwmark, if specified */ |
103 | if (IS_RULE_FILTERING_ON_FWMARK(rule)) { | |
104 | addattr32(&req.n, sizeof(req), FRA_FWMARK, | |
105 | rule->rule.filter.fwmark); | |
106 | } | |
107 | ||
942bf97b | 108 | /* Route table to use to forward, if filter criteria matches. */ |
5dd0722d PG |
109 | if (rule->rule.action.table < 256) |
110 | req.frh.table = rule->rule.action.table; | |
942bf97b | 111 | else { |
112 | req.frh.table = RT_TABLE_UNSPEC; | |
113 | addattr32(&req.n, sizeof(req), FRA_TABLE, | |
5dd0722d | 114 | rule->rule.action.table); |
942bf97b | 115 | } |
116 | ||
117 | if (IS_ZEBRA_DEBUG_KERNEL) | |
fd71d73e DS |
118 | zlog_debug( |
119 | "Tx %s family %s IF %s(%u) Pref %u Src %s Dst %s Table %u", | |
120 | nl_msg_type_to_str(cmd), nl_family_to_str(family), | |
a0321978 | 121 | rule->ifp ? rule->ifp->name : "Unknown", |
5dd0722d PG |
122 | rule->ifp ? rule->ifp->ifindex : 0, rule->rule.priority, |
123 | prefix2str(&rule->rule.filter.src_ip, buf1, | |
124 | sizeof(buf1)), | |
125 | prefix2str(&rule->rule.filter.dst_ip, buf2, | |
126 | sizeof(buf2)), | |
127 | rule->rule.action.table); | |
942bf97b | 128 | |
129 | /* Ship off the message. | |
130 | * Note: Currently, netlink_talk() is a blocking call which returns | |
131 | * back the status. | |
132 | */ | |
133 | memset(&snl, 0, sizeof(snl)); | |
134 | snl.nl_family = AF_NETLINK; | |
135 | return netlink_talk(netlink_talk_filter, &req.n, | |
136 | &zns->netlink_cmd, zns, 0); | |
137 | } | |
138 | ||
139 | ||
140 | /* Public functions */ | |
141 | /* | |
142 | * Install specified rule for a specific interface. The preference is what | |
143 | * goes in the rule to denote relative ordering; it may or may not be the | |
144 | * same as the rule's user-defined sequence number. | |
145 | */ | |
ebecd649 | 146 | enum dp_req_result kernel_add_pbr_rule(struct zebra_pbr_rule *rule) |
942bf97b | 147 | { |
148 | int ret = 0; | |
149 | ||
a0321978 DS |
150 | ret = netlink_rule_update(RTM_NEWRULE, rule); |
151 | kernel_pbr_rule_add_del_status(rule, | |
215181cb DS |
152 | (!ret) ? DP_INSTALL_SUCCESS |
153 | : DP_INSTALL_FAILURE); | |
ebecd649 DS |
154 | |
155 | return DP_REQUEST_SUCCESS; | |
942bf97b | 156 | } |
157 | ||
158 | /* | |
159 | * Uninstall specified rule for a specific interface. | |
160 | */ | |
ebecd649 | 161 | enum dp_req_result kernel_del_pbr_rule(struct zebra_pbr_rule *rule) |
942bf97b | 162 | { |
163 | int ret = 0; | |
164 | ||
a0321978 DS |
165 | ret = netlink_rule_update(RTM_DELRULE, rule); |
166 | kernel_pbr_rule_add_del_status(rule, | |
215181cb DS |
167 | (!ret) ? DP_DELETE_SUCCESS |
168 | : DP_DELETE_FAILURE); | |
ebecd649 DS |
169 | |
170 | return DP_REQUEST_SUCCESS; | |
942bf97b | 171 | } |
172 | ||
173 | /* | |
174 | * Handle netlink notification informing a rule add or delete. | |
175 | * Handling of an ADD is TBD. | |
176 | * DELs are notified up, if other attributes indicate it may be a | |
177 | * notification of interest. The expectation is that if this corresponds | |
178 | * to a PBR rule added by FRR, it will be readded. | |
179 | */ | |
2414abd3 | 180 | int netlink_rule_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) |
942bf97b | 181 | { |
182 | struct zebra_ns *zns; | |
183 | struct fib_rule_hdr *frh; | |
184 | struct rtattr *tb[FRA_MAX + 1]; | |
185 | int len; | |
186 | char *ifname; | |
942bf97b | 187 | struct zebra_pbr_rule rule; |
188 | char buf1[PREFIX_STRLEN]; | |
189 | char buf2[PREFIX_STRLEN]; | |
190 | ||
191 | /* Basic validation followed by extracting attributes. */ | |
192 | if (h->nlmsg_type != RTM_NEWRULE && h->nlmsg_type != RTM_DELRULE) | |
193 | return 0; | |
194 | ||
195 | /* TBD */ | |
196 | if (h->nlmsg_type == RTM_NEWRULE) | |
197 | return 0; | |
198 | ||
199 | len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct fib_rule_hdr)); | |
9bdf8618 DS |
200 | if (len < 0) { |
201 | zlog_err("%s: Message received from netlink is of a broken size: %d %zu", | |
202 | __PRETTY_FUNCTION__, h->nlmsg_len, | |
203 | (size_t)NLMSG_LENGTH(sizeof(struct fib_rule_hdr))); | |
942bf97b | 204 | return -1; |
9bdf8618 | 205 | } |
942bf97b | 206 | |
207 | frh = NLMSG_DATA(h); | |
8a1b681c | 208 | if (frh->family != AF_INET && frh->family != AF_INET6) { |
9df414fe | 209 | flog_warn( |
e914ccbe | 210 | EC_ZEBRA_NETLINK_INVALID_AF, |
81227874 | 211 | "Invalid address family: %u received from kernel rule change: %u", |
8a1b681c | 212 | frh->family, h->nlmsg_type); |
942bf97b | 213 | return 0; |
8a1b681c | 214 | } |
942bf97b | 215 | if (frh->action != FR_ACT_TO_TBL) |
216 | return 0; | |
217 | ||
218 | memset(tb, 0, sizeof(tb)); | |
219 | netlink_parse_rtattr(tb, FRA_MAX, RTM_RTA(frh), len); | |
220 | ||
221 | /* TBD: We don't care about rules not specifying an IIF. */ | |
222 | if (tb[FRA_IFNAME] == NULL) | |
223 | return 0; | |
224 | ||
225 | /* If we don't know the interface, we don't care. */ | |
226 | ifname = (char *)RTA_DATA(tb[FRA_IFNAME]); | |
227 | zns = zebra_ns_lookup(ns_id); | |
a0321978 DS |
228 | rule.ifp = if_lookup_by_name_per_ns(zns, ifname); |
229 | if (!rule.ifp) | |
942bf97b | 230 | return 0; |
231 | ||
232 | memset(&rule, 0, sizeof(rule)); | |
233 | if (tb[FRA_PRIORITY]) | |
5dd0722d | 234 | rule.rule.priority = *(uint32_t *)RTA_DATA(tb[FRA_PRIORITY]); |
942bf97b | 235 | |
236 | if (tb[FRA_SRC]) { | |
237 | if (frh->family == AF_INET) | |
5dd0722d | 238 | memcpy(&rule.rule.filter.src_ip.u.prefix4, |
942bf97b | 239 | RTA_DATA(tb[FRA_SRC]), 4); |
240 | else | |
5dd0722d | 241 | memcpy(&rule.rule.filter.src_ip.u.prefix6, |
942bf97b | 242 | RTA_DATA(tb[FRA_SRC]), 16); |
5dd0722d PG |
243 | rule.rule.filter.src_ip.prefixlen = frh->src_len; |
244 | rule.rule.filter.filter_bm |= PBR_FILTER_SRC_IP; | |
942bf97b | 245 | } |
246 | ||
247 | if (tb[FRA_DST]) { | |
248 | if (frh->family == AF_INET) | |
5dd0722d | 249 | memcpy(&rule.rule.filter.dst_ip.u.prefix4, |
942bf97b | 250 | RTA_DATA(tb[FRA_DST]), 4); |
251 | else | |
5dd0722d | 252 | memcpy(&rule.rule.filter.dst_ip.u.prefix6, |
942bf97b | 253 | RTA_DATA(tb[FRA_DST]), 16); |
5dd0722d PG |
254 | rule.rule.filter.dst_ip.prefixlen = frh->dst_len; |
255 | rule.rule.filter.filter_bm |= PBR_FILTER_DST_IP; | |
942bf97b | 256 | } |
257 | ||
258 | if (tb[FRA_TABLE]) | |
5dd0722d | 259 | rule.rule.action.table = *(uint32_t *)RTA_DATA(tb[FRA_TABLE]); |
942bf97b | 260 | else |
5dd0722d | 261 | rule.rule.action.table = frh->table; |
942bf97b | 262 | |
263 | if (IS_ZEBRA_DEBUG_KERNEL) | |
fd71d73e DS |
264 | zlog_debug( |
265 | "Rx %s family %s IF %s(%u) Pref %u Src %s Dst %s Table %u", | |
266 | nl_msg_type_to_str(h->nlmsg_type), | |
a0321978 | 267 | nl_family_to_str(frh->family), rule.ifp->name, |
5dd0722d PG |
268 | rule.ifp->ifindex, rule.rule.priority, |
269 | prefix2str(&rule.rule.filter.src_ip, buf1, | |
270 | sizeof(buf1)), | |
271 | prefix2str(&rule.rule.filter.dst_ip, buf2, | |
272 | sizeof(buf2)), | |
273 | rule.rule.action.table); | |
fd71d73e | 274 | |
a0321978 | 275 | return kernel_pbr_rule_del(&rule); |
942bf97b | 276 | } |
277 | ||
278 | /* | |
279 | * Get to know existing PBR rules in the kernel - typically called at startup. | |
280 | * TBD. | |
281 | */ | |
282 | int netlink_rules_read(struct zebra_ns *zns) | |
283 | { | |
284 | return 0; | |
285 | } | |
286 | ||
287 | #endif /* HAVE_NETLINK */ |