]>
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 | ||
101 | /* Route table to use to forward, if filter criteria matches. */ | |
5dd0722d PG |
102 | if (rule->rule.action.table < 256) |
103 | req.frh.table = rule->rule.action.table; | |
942bf97b | 104 | else { |
105 | req.frh.table = RT_TABLE_UNSPEC; | |
106 | addattr32(&req.n, sizeof(req), FRA_TABLE, | |
5dd0722d | 107 | rule->rule.action.table); |
942bf97b | 108 | } |
109 | ||
110 | if (IS_ZEBRA_DEBUG_KERNEL) | |
fd71d73e DS |
111 | zlog_debug( |
112 | "Tx %s family %s IF %s(%u) Pref %u Src %s Dst %s Table %u", | |
113 | nl_msg_type_to_str(cmd), nl_family_to_str(family), | |
a0321978 | 114 | rule->ifp ? rule->ifp->name : "Unknown", |
5dd0722d PG |
115 | rule->ifp ? rule->ifp->ifindex : 0, rule->rule.priority, |
116 | prefix2str(&rule->rule.filter.src_ip, buf1, | |
117 | sizeof(buf1)), | |
118 | prefix2str(&rule->rule.filter.dst_ip, buf2, | |
119 | sizeof(buf2)), | |
120 | rule->rule.action.table); | |
942bf97b | 121 | |
122 | /* Ship off the message. | |
123 | * Note: Currently, netlink_talk() is a blocking call which returns | |
124 | * back the status. | |
125 | */ | |
126 | memset(&snl, 0, sizeof(snl)); | |
127 | snl.nl_family = AF_NETLINK; | |
128 | return netlink_talk(netlink_talk_filter, &req.n, | |
129 | &zns->netlink_cmd, zns, 0); | |
130 | } | |
131 | ||
132 | ||
133 | /* Public functions */ | |
134 | /* | |
135 | * Install specified rule for a specific interface. The preference is what | |
136 | * goes in the rule to denote relative ordering; it may or may not be the | |
137 | * same as the rule's user-defined sequence number. | |
138 | */ | |
a0321978 | 139 | void kernel_add_pbr_rule(struct zebra_pbr_rule *rule) |
942bf97b | 140 | { |
141 | int ret = 0; | |
142 | ||
a0321978 DS |
143 | ret = netlink_rule_update(RTM_NEWRULE, rule); |
144 | kernel_pbr_rule_add_del_status(rule, | |
fd71d73e DS |
145 | (!ret) ? SOUTHBOUND_INSTALL_SUCCESS |
146 | : SOUTHBOUND_INSTALL_FAILURE); | |
942bf97b | 147 | } |
148 | ||
149 | /* | |
150 | * Uninstall specified rule for a specific interface. | |
151 | */ | |
a0321978 | 152 | void kernel_del_pbr_rule(struct zebra_pbr_rule *rule) |
942bf97b | 153 | { |
154 | int ret = 0; | |
155 | ||
a0321978 DS |
156 | ret = netlink_rule_update(RTM_DELRULE, rule); |
157 | kernel_pbr_rule_add_del_status(rule, | |
fd71d73e DS |
158 | (!ret) ? SOUTHBOUND_DELETE_SUCCESS |
159 | : SOUTHBOUND_DELETE_FAILURE); | |
942bf97b | 160 | } |
161 | ||
162 | /* | |
163 | * Handle netlink notification informing a rule add or delete. | |
164 | * Handling of an ADD is TBD. | |
165 | * DELs are notified up, if other attributes indicate it may be a | |
166 | * notification of interest. The expectation is that if this corresponds | |
167 | * to a PBR rule added by FRR, it will be readded. | |
168 | */ | |
169 | int netlink_rule_change(struct sockaddr_nl *snl, struct nlmsghdr *h, | |
170 | ns_id_t ns_id, int startup) | |
171 | { | |
172 | struct zebra_ns *zns; | |
173 | struct fib_rule_hdr *frh; | |
174 | struct rtattr *tb[FRA_MAX + 1]; | |
175 | int len; | |
176 | char *ifname; | |
942bf97b | 177 | struct zebra_pbr_rule rule; |
178 | char buf1[PREFIX_STRLEN]; | |
179 | char buf2[PREFIX_STRLEN]; | |
180 | ||
181 | /* Basic validation followed by extracting attributes. */ | |
182 | if (h->nlmsg_type != RTM_NEWRULE && h->nlmsg_type != RTM_DELRULE) | |
183 | return 0; | |
184 | ||
185 | /* TBD */ | |
186 | if (h->nlmsg_type == RTM_NEWRULE) | |
187 | return 0; | |
188 | ||
189 | len = h->nlmsg_len - NLMSG_LENGTH(sizeof(struct fib_rule_hdr)); | |
190 | if (len < 0) | |
191 | return -1; | |
192 | ||
193 | frh = NLMSG_DATA(h); | |
194 | if (frh->family != AF_INET && frh->family != AF_INET6) | |
195 | return 0; | |
196 | if (frh->action != FR_ACT_TO_TBL) | |
197 | return 0; | |
198 | ||
199 | memset(tb, 0, sizeof(tb)); | |
200 | netlink_parse_rtattr(tb, FRA_MAX, RTM_RTA(frh), len); | |
201 | ||
202 | /* TBD: We don't care about rules not specifying an IIF. */ | |
203 | if (tb[FRA_IFNAME] == NULL) | |
204 | return 0; | |
205 | ||
206 | /* If we don't know the interface, we don't care. */ | |
207 | ifname = (char *)RTA_DATA(tb[FRA_IFNAME]); | |
208 | zns = zebra_ns_lookup(ns_id); | |
a0321978 DS |
209 | rule.ifp = if_lookup_by_name_per_ns(zns, ifname); |
210 | if (!rule.ifp) | |
942bf97b | 211 | return 0; |
212 | ||
213 | memset(&rule, 0, sizeof(rule)); | |
214 | if (tb[FRA_PRIORITY]) | |
5dd0722d | 215 | rule.rule.priority = *(uint32_t *)RTA_DATA(tb[FRA_PRIORITY]); |
942bf97b | 216 | |
217 | if (tb[FRA_SRC]) { | |
218 | if (frh->family == AF_INET) | |
5dd0722d | 219 | memcpy(&rule.rule.filter.src_ip.u.prefix4, |
942bf97b | 220 | RTA_DATA(tb[FRA_SRC]), 4); |
221 | else | |
5dd0722d | 222 | memcpy(&rule.rule.filter.src_ip.u.prefix6, |
942bf97b | 223 | RTA_DATA(tb[FRA_SRC]), 16); |
5dd0722d PG |
224 | rule.rule.filter.src_ip.prefixlen = frh->src_len; |
225 | rule.rule.filter.filter_bm |= PBR_FILTER_SRC_IP; | |
942bf97b | 226 | } |
227 | ||
228 | if (tb[FRA_DST]) { | |
229 | if (frh->family == AF_INET) | |
5dd0722d | 230 | memcpy(&rule.rule.filter.dst_ip.u.prefix4, |
942bf97b | 231 | RTA_DATA(tb[FRA_DST]), 4); |
232 | else | |
5dd0722d | 233 | memcpy(&rule.rule.filter.dst_ip.u.prefix6, |
942bf97b | 234 | RTA_DATA(tb[FRA_DST]), 16); |
5dd0722d PG |
235 | rule.rule.filter.dst_ip.prefixlen = frh->dst_len; |
236 | rule.rule.filter.filter_bm |= PBR_FILTER_DST_IP; | |
942bf97b | 237 | } |
238 | ||
239 | if (tb[FRA_TABLE]) | |
5dd0722d | 240 | rule.rule.action.table = *(uint32_t *)RTA_DATA(tb[FRA_TABLE]); |
942bf97b | 241 | else |
5dd0722d | 242 | rule.rule.action.table = frh->table; |
942bf97b | 243 | |
244 | if (IS_ZEBRA_DEBUG_KERNEL) | |
fd71d73e DS |
245 | zlog_debug( |
246 | "Rx %s family %s IF %s(%u) Pref %u Src %s Dst %s Table %u", | |
247 | nl_msg_type_to_str(h->nlmsg_type), | |
a0321978 | 248 | nl_family_to_str(frh->family), rule.ifp->name, |
5dd0722d PG |
249 | rule.ifp->ifindex, rule.rule.priority, |
250 | prefix2str(&rule.rule.filter.src_ip, buf1, | |
251 | sizeof(buf1)), | |
252 | prefix2str(&rule.rule.filter.dst_ip, buf2, | |
253 | sizeof(buf2)), | |
254 | rule.rule.action.table); | |
fd71d73e | 255 | |
a0321978 | 256 | return kernel_pbr_rule_del(&rule); |
942bf97b | 257 | } |
258 | ||
259 | /* | |
260 | * Get to know existing PBR rules in the kernel - typically called at startup. | |
261 | * TBD. | |
262 | */ | |
263 | int netlink_rules_read(struct zebra_ns *zns) | |
264 | { | |
265 | return 0; | |
266 | } | |
267 | ||
268 | #endif /* HAVE_NETLINK */ |