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