]>
Commit | Line | Data |
---|---|---|
942bf97b | 1 | /* Zebra Policy Based Routing (PBR) main handling. |
2 | * Copyright (C) 2018 Cumulus Networks, Inc. | |
3 | * | |
4 | * This file is part of FRR. | |
5 | * | |
6 | * FRR 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 | |
9 | * later version. | |
10 | * | |
11 | * FRR 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. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with FRR; see the file COPYING. If not, write to the Free | |
18 | * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | |
19 | * 02111-1307, USA. | |
20 | */ | |
21 | ||
22 | #include <zebra.h> | |
23 | ||
43fe6a2a DS |
24 | #include <jhash.h> |
25 | #include <hash.h> | |
f80ec7e3 | 26 | #include <memory.h> |
73a829f7 | 27 | #include <hook.h> |
43fe6a2a | 28 | |
7f0ea8a4 | 29 | #include "zebra/zebra_router.h" |
942bf97b | 30 | #include "zebra/zebra_pbr.h" |
31 | #include "zebra/rt.h" | |
bf094f69 | 32 | #include "zebra/zapi_msg.h" |
f80ec7e3 | 33 | #include "zebra/zebra_memory.h" |
c0ea1ae7 | 34 | #include "zebra/zserv.h" |
f80ec7e3 PG |
35 | |
36 | /* definitions */ | |
37 | DEFINE_MTYPE_STATIC(ZEBRA, PBR_IPTABLE_IFNAME, "PBR interface list") | |
942bf97b | 38 | |
39 | /* definitions */ | |
586f4ccf PG |
40 | static const struct message ipset_type_msg[] = { |
41 | {IPSET_NET_PORT_NET, "net,port,net"}, | |
42 | {IPSET_NET_PORT, "net,port"}, | |
43 | {IPSET_NET_NET, "net,net"}, | |
44 | {IPSET_NET, "net"}, | |
45 | {0} | |
46 | }; | |
942bf97b | 47 | |
be729dd7 PG |
48 | const struct message icmp_typecode_str[] = { |
49 | { 0 << 8, "echo-reply"}, | |
50 | { 0 << 8, "pong"}, | |
51 | { 3 << 8, "network-unreachable"}, | |
52 | { (3 << 8) + 1, "host-unreachable"}, | |
53 | { (3 << 8) + 2, "protocol-unreachable"}, | |
54 | { (3 << 8) + 3, "port-unreachable"}, | |
55 | { (3 << 8) + 4, "fragmentation-needed"}, | |
56 | { (3 << 8) + 5, "source-route-failed"}, | |
57 | { (3 << 8) + 6, "network-unknown"}, | |
58 | { (3 << 8) + 7, "host-unknown"}, | |
59 | { (3 << 8) + 9, "network-prohibited"}, | |
60 | { (3 << 8) + 10, "host-prohibited"}, | |
61 | { (3 << 8) + 11, "TOS-network-unreachable"}, | |
62 | { (3 << 8) + 12, "TOS-host-unreachable"}, | |
63 | { (3 << 8) + 13, "communication-prohibited"}, | |
64 | { (3 << 8) + 14, "host-precedence-violation"}, | |
65 | { (3 << 8) + 15, "precedence-cutoff"}, | |
66 | { 4 << 8, "source-quench"}, | |
67 | { 5 << 8, "network-redirect"}, | |
68 | { (5 << 8) + 1, "host-redirect"}, | |
69 | { (5 << 8) + 2, "TOS-network-redirect"}, | |
70 | { (5 << 8) + 3, "TOS-host-redirect"}, | |
71 | { 8 << 8, "echo-request"}, | |
72 | { 8 << 8, "ping"}, | |
73 | { 9 << 8, "router-advertisement"}, | |
74 | { 10 << 8, "router-solicitation"}, | |
75 | { 11 << 8, "ttl-zero-during-transit"}, | |
76 | { (11 << 8) + 1, "ttl-zero-during-reassembly"}, | |
77 | { 12 << 8, "ip-header-bad"}, | |
78 | { (12 << 8) + 1, "required-option-missing"}, | |
79 | { 13 << 8, "timestamp-request"}, | |
80 | { 14 << 8, "timestamp-reply"}, | |
81 | { 17 << 8, "address-mask-request"}, | |
82 | { 18 << 8, "address-mask-reply"}, | |
83 | {0} | |
84 | }; | |
85 | ||
c9b1139a PG |
86 | const struct message icmpv6_typecode_str[] = { |
87 | { 128 << 8, "echo-request"}, | |
88 | { 129 << 8, "echo-reply"}, | |
89 | { 1 << 8, "no-route"}, | |
90 | { (1 << 8) + 1, "communication-prohibited"}, | |
91 | { (1 << 8) + 3, "address-unreachable"}, | |
92 | { (1 << 8) + 4, "port-unreachable"}, | |
93 | { (2 << 8), "packet-too-big"}, | |
94 | { 3 << 0, "ttl-zero-during-transit"}, | |
95 | { (3 << 8) + 1, "ttl-zero-during-reassembly"}, | |
96 | { 4 << 0, "bad-header"}, | |
97 | { (4 << 0) + 1, "unknown-header-type"}, | |
98 | { (4 << 0) + 2, "unknown-option"}, | |
99 | { 133 << 8, "router-solicitation"}, | |
100 | { 134 << 8, "router-advertisement"}, | |
101 | { 135 << 8, "neighbor-solicitation"}, | |
102 | { 136 << 8, "neighbor-advertisement"}, | |
103 | { 137 << 8, "redirect"}, | |
104 | {0} | |
105 | }; | |
106 | ||
dc993e76 PG |
107 | /* definitions */ |
108 | static const struct message tcp_value_str[] = { | |
109 | {TCP_HEADER_FIN, "FIN"}, | |
110 | {TCP_HEADER_SYN, "SYN"}, | |
111 | {TCP_HEADER_RST, "RST"}, | |
112 | {TCP_HEADER_PSH, "PSH"}, | |
113 | {TCP_HEADER_ACK, "ACK"}, | |
114 | {TCP_HEADER_URG, "URG"}, | |
115 | {0} | |
116 | }; | |
117 | ||
5ac5b7cc PG |
118 | static const struct message fragment_value_str[] = { |
119 | {1, "dont-fragment"}, | |
120 | {2, "is-fragment"}, | |
121 | {4, "first-fragment"}, | |
122 | {8, "last-fragment"}, | |
123 | {0} | |
124 | }; | |
125 | ||
942bf97b | 126 | /* static function declarations */ |
1c6fca1f | 127 | DEFINE_HOOK(zebra_pbr_ipset_entry_get_stat, |
62f20a52 DS |
128 | (struct zebra_pbr_ipset_entry *ipset, uint64_t *pkts, |
129 | uint64_t *bytes), | |
130 | (ipset, pkts, bytes)) | |
131 | ||
1c6fca1f | 132 | DEFINE_HOOK(zebra_pbr_iptable_get_stat, |
62f20a52 DS |
133 | (struct zebra_pbr_iptable *iptable, uint64_t *pkts, |
134 | uint64_t *bytes), | |
135 | (iptable, pkts, bytes)) | |
136 | ||
1c6fca1f | 137 | DEFINE_HOOK(zebra_pbr_iptable_update, |
62f20a52 DS |
138 | (int cmd, struct zebra_pbr_iptable *iptable), (cmd, iptable)); |
139 | ||
1c6fca1f | 140 | DEFINE_HOOK(zebra_pbr_ipset_entry_update, |
62f20a52 DS |
141 | (int cmd, struct zebra_pbr_ipset_entry *ipset), (cmd, ipset)); |
142 | ||
1c6fca1f | 143 | DEFINE_HOOK(zebra_pbr_ipset_update, |
62f20a52 | 144 | (int cmd, struct zebra_pbr_ipset *ipset), (cmd, ipset)); |
942bf97b | 145 | |
146 | /* Private functions */ | |
147 | ||
148 | /* Public functions */ | |
43fe6a2a | 149 | void zebra_pbr_rules_free(void *arg) |
1fbfe5a5 | 150 | { |
43fe6a2a DS |
151 | struct zebra_pbr_rule *rule; |
152 | ||
153 | rule = (struct zebra_pbr_rule *)arg; | |
154 | ||
f62e5480 | 155 | (void)dplane_pbr_rule_delete(rule); |
43fe6a2a DS |
156 | XFREE(MTYPE_TMP, rule); |
157 | } | |
158 | ||
d8b87afe | 159 | uint32_t zebra_pbr_rules_hash_key(const void *arg) |
43fe6a2a | 160 | { |
d8b87afe | 161 | const struct zebra_pbr_rule *rule; |
43fe6a2a DS |
162 | uint32_t key; |
163 | ||
d8b87afe | 164 | rule = arg; |
5dd0722d PG |
165 | key = jhash_3words(rule->rule.seq, rule->rule.priority, |
166 | rule->rule.action.table, | |
167 | prefix_hash_key(&rule->rule.filter.src_ip)); | |
a0321978 | 168 | |
5dd0722d | 169 | if (rule->rule.filter.fwmark) |
58a1d249 | 170 | key = jhash_2words(rule->rule.filter.fwmark, rule->vrf_id, key); |
1907e4b8 | 171 | else |
58a1d249 DS |
172 | key = jhash_1word(rule->vrf_id, key); |
173 | ||
174 | key = jhash(rule->ifname, strlen(rule->ifname), key); | |
7f0ea8a4 | 175 | |
5dd0722d PG |
176 | return jhash_3words(rule->rule.filter.src_port, |
177 | rule->rule.filter.dst_port, | |
178 | prefix_hash_key(&rule->rule.filter.dst_ip), | |
179 | jhash_1word(rule->rule.unique, key)); | |
43fe6a2a DS |
180 | } |
181 | ||
74df8d6d | 182 | bool zebra_pbr_rules_hash_equal(const void *arg1, const void *arg2) |
43fe6a2a DS |
183 | { |
184 | const struct zebra_pbr_rule *r1, *r2; | |
185 | ||
186 | r1 = (const struct zebra_pbr_rule *)arg1; | |
187 | r2 = (const struct zebra_pbr_rule *)arg2; | |
188 | ||
5dd0722d | 189 | if (r1->rule.seq != r2->rule.seq) |
74df8d6d | 190 | return false; |
43fe6a2a | 191 | |
5dd0722d | 192 | if (r1->rule.priority != r2->rule.priority) |
74df8d6d | 193 | return false; |
43fe6a2a | 194 | |
5dd0722d | 195 | if (r1->rule.unique != r2->rule.unique) |
74df8d6d | 196 | return false; |
b6c5d343 | 197 | |
5dd0722d | 198 | if (r1->rule.action.table != r2->rule.action.table) |
74df8d6d | 199 | return false; |
43fe6a2a | 200 | |
5dd0722d | 201 | if (r1->rule.filter.src_port != r2->rule.filter.src_port) |
74df8d6d | 202 | return false; |
43fe6a2a | 203 | |
5dd0722d | 204 | if (r1->rule.filter.dst_port != r2->rule.filter.dst_port) |
74df8d6d | 205 | return false; |
43fe6a2a | 206 | |
5dd0722d | 207 | if (r1->rule.filter.fwmark != r2->rule.filter.fwmark) |
74df8d6d | 208 | return false; |
1907e4b8 | 209 | |
5dd0722d | 210 | if (!prefix_same(&r1->rule.filter.src_ip, &r2->rule.filter.src_ip)) |
74df8d6d | 211 | return false; |
43fe6a2a | 212 | |
5dd0722d | 213 | if (!prefix_same(&r1->rule.filter.dst_ip, &r2->rule.filter.dst_ip)) |
74df8d6d | 214 | return false; |
43fe6a2a | 215 | |
58a1d249 | 216 | if (strcmp(r1->rule.ifname, r2->rule.ifname) != 0) |
74df8d6d | 217 | return false; |
a0321978 | 218 | |
7f0ea8a4 DS |
219 | if (r1->vrf_id != r2->vrf_id) |
220 | return false; | |
221 | ||
74df8d6d | 222 | return true; |
43fe6a2a DS |
223 | } |
224 | ||
f46bbab4 | 225 | struct pbr_rule_unique_lookup { |
8c3cd6c6 DS |
226 | struct zebra_pbr_rule *rule; |
227 | uint32_t unique; | |
58a1d249 | 228 | char ifname[INTERFACE_NAMSIZ + 1]; |
7f0ea8a4 | 229 | vrf_id_t vrf_id; |
8c3cd6c6 DS |
230 | }; |
231 | ||
e3b78da8 | 232 | static int pbr_rule_lookup_unique_walker(struct hash_bucket *b, void *data) |
8c3cd6c6 | 233 | { |
f46bbab4 | 234 | struct pbr_rule_unique_lookup *pul = data; |
8c3cd6c6 DS |
235 | struct zebra_pbr_rule *rule = b->data; |
236 | ||
7f0ea8a4 | 237 | if (pul->unique == rule->rule.unique |
58a1d249 | 238 | && strncmp(pul->ifname, rule->rule.ifname, INTERFACE_NAMSIZ) == 0 |
7f0ea8a4 | 239 | && pul->vrf_id == rule->vrf_id) { |
8c3cd6c6 DS |
240 | pul->rule = rule; |
241 | return HASHWALK_ABORT; | |
242 | } | |
243 | ||
244 | return HASHWALK_CONTINUE; | |
245 | } | |
246 | ||
62f20a52 DS |
247 | static struct zebra_pbr_rule * |
248 | pbr_rule_lookup_unique(struct zebra_pbr_rule *zrule) | |
8c3cd6c6 | 249 | { |
f46bbab4 | 250 | struct pbr_rule_unique_lookup pul; |
8c3cd6c6 | 251 | |
7f0ea8a4 | 252 | pul.unique = zrule->rule.unique; |
58a1d249 | 253 | strlcpy(pul.ifname, zrule->rule.ifname, INTERFACE_NAMSIZ); |
8c3cd6c6 | 254 | pul.rule = NULL; |
7f0ea8a4 DS |
255 | pul.vrf_id = zrule->vrf_id; |
256 | hash_walk(zrouter.rules_hash, &pbr_rule_lookup_unique_walker, &pul); | |
8c3cd6c6 DS |
257 | |
258 | return pul.rule; | |
259 | } | |
260 | ||
7661461a PG |
261 | void zebra_pbr_ipset_free(void *arg) |
262 | { | |
263 | struct zebra_pbr_ipset *ipset; | |
264 | ||
265 | ipset = (struct zebra_pbr_ipset *)arg; | |
1c6fca1f | 266 | hook_call(zebra_pbr_ipset_update, 0, ipset); |
7661461a PG |
267 | XFREE(MTYPE_TMP, ipset); |
268 | } | |
269 | ||
d8b87afe | 270 | uint32_t zebra_pbr_ipset_hash_key(const void *arg) |
7661461a | 271 | { |
d8b87afe | 272 | const struct zebra_pbr_ipset *ipset = arg; |
425bdd6b | 273 | uint32_t *pnt = (uint32_t *)&ipset->ipset_name; |
62f20a52 | 274 | uint32_t key = jhash_1word(ipset->vrf_id, 0x63ab42de); |
7661461a | 275 | |
a60b7031 PG |
276 | key = jhash_1word(ipset->family, key); |
277 | ||
62f20a52 | 278 | return jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE, key); |
7661461a PG |
279 | } |
280 | ||
74df8d6d | 281 | bool zebra_pbr_ipset_hash_equal(const void *arg1, const void *arg2) |
7661461a PG |
282 | { |
283 | const struct zebra_pbr_ipset *r1, *r2; | |
284 | ||
285 | r1 = (const struct zebra_pbr_ipset *)arg1; | |
286 | r2 = (const struct zebra_pbr_ipset *)arg2; | |
287 | ||
288 | if (r1->type != r2->type) | |
74df8d6d | 289 | return false; |
7661461a | 290 | if (r1->unique != r2->unique) |
74df8d6d | 291 | return false; |
62f20a52 DS |
292 | if (r1->vrf_id != r2->vrf_id) |
293 | return false; | |
a60b7031 PG |
294 | if (r1->family != r2->family) |
295 | return false; | |
62f20a52 | 296 | |
7661461a PG |
297 | if (strncmp(r1->ipset_name, r2->ipset_name, |
298 | ZEBRA_IPSET_NAME_SIZE)) | |
74df8d6d DS |
299 | return false; |
300 | return true; | |
7661461a PG |
301 | } |
302 | ||
303 | void zebra_pbr_ipset_entry_free(void *arg) | |
304 | { | |
305 | struct zebra_pbr_ipset_entry *ipset; | |
306 | ||
307 | ipset = (struct zebra_pbr_ipset_entry *)arg; | |
73a829f7 | 308 | |
1c6fca1f | 309 | hook_call(zebra_pbr_ipset_entry_update, 0, ipset); |
7661461a PG |
310 | |
311 | XFREE(MTYPE_TMP, ipset); | |
312 | } | |
313 | ||
d8b87afe | 314 | uint32_t zebra_pbr_ipset_entry_hash_key(const void *arg) |
7661461a | 315 | { |
d8b87afe | 316 | const struct zebra_pbr_ipset_entry *ipset; |
7661461a PG |
317 | uint32_t key; |
318 | ||
d8b87afe | 319 | ipset = arg; |
7661461a PG |
320 | key = prefix_hash_key(&ipset->src); |
321 | key = jhash_1word(ipset->unique, key); | |
322 | key = jhash_1word(prefix_hash_key(&ipset->dst), key); | |
25d760c5 PG |
323 | key = jhash(&ipset->dst_port_min, 2, key); |
324 | key = jhash(&ipset->dst_port_max, 2, key); | |
325 | key = jhash(&ipset->src_port_min, 2, key); | |
326 | key = jhash(&ipset->src_port_max, 2, key); | |
327 | key = jhash(&ipset->proto, 1, key); | |
7661461a PG |
328 | |
329 | return key; | |
330 | } | |
331 | ||
74df8d6d | 332 | bool zebra_pbr_ipset_entry_hash_equal(const void *arg1, const void *arg2) |
7661461a PG |
333 | { |
334 | const struct zebra_pbr_ipset_entry *r1, *r2; | |
335 | ||
336 | r1 = (const struct zebra_pbr_ipset_entry *)arg1; | |
337 | r2 = (const struct zebra_pbr_ipset_entry *)arg2; | |
338 | ||
339 | if (r1->unique != r2->unique) | |
74df8d6d | 340 | return false; |
7661461a PG |
341 | |
342 | if (!prefix_same(&r1->src, &r2->src)) | |
74df8d6d | 343 | return false; |
7661461a PG |
344 | |
345 | if (!prefix_same(&r1->dst, &r2->dst)) | |
74df8d6d | 346 | return false; |
7661461a | 347 | |
25d760c5 | 348 | if (r1->src_port_min != r2->src_port_min) |
74df8d6d | 349 | return false; |
25d760c5 PG |
350 | |
351 | if (r1->src_port_max != r2->src_port_max) | |
74df8d6d | 352 | return false; |
25d760c5 PG |
353 | |
354 | if (r1->dst_port_min != r2->dst_port_min) | |
74df8d6d | 355 | return false; |
25d760c5 PG |
356 | |
357 | if (r1->dst_port_max != r2->dst_port_max) | |
74df8d6d | 358 | return false; |
25d760c5 PG |
359 | |
360 | if (r1->proto != r2->proto) | |
74df8d6d DS |
361 | return false; |
362 | return true; | |
7661461a PG |
363 | } |
364 | ||
3b1de7b8 PG |
365 | /* this function gives option to flush plugin memory contexts |
366 | * with all parameter. set it to true to flush all | |
367 | * set it to false to flush only passed arg argument | |
368 | */ | |
369 | static void _zebra_pbr_iptable_free_all(void *arg, bool all) | |
7abd6c4f PG |
370 | { |
371 | struct zebra_pbr_iptable *iptable; | |
f80ec7e3 PG |
372 | struct listnode *node, *nnode; |
373 | char *name; | |
7abd6c4f PG |
374 | |
375 | iptable = (struct zebra_pbr_iptable *)arg; | |
3b1de7b8 PG |
376 | |
377 | if (all) | |
378 | hook_call(zebra_pbr_iptable_update, 0, iptable); | |
7abd6c4f | 379 | |
8b5c4dce QY |
380 | if (iptable->interface_name_list) { |
381 | for (ALL_LIST_ELEMENTS(iptable->interface_name_list, node, | |
382 | nnode, name)) { | |
383 | XFREE(MTYPE_PBR_IPTABLE_IFNAME, name); | |
384 | list_delete_node(iptable->interface_name_list, node); | |
385 | } | |
386 | list_delete(&iptable->interface_name_list); | |
f80ec7e3 | 387 | } |
7abd6c4f PG |
388 | XFREE(MTYPE_TMP, iptable); |
389 | } | |
390 | ||
3b1de7b8 PG |
391 | void zebra_pbr_iptable_free(void *arg) |
392 | { | |
393 | _zebra_pbr_iptable_free_all(arg, false); | |
394 | } | |
395 | ||
d8b87afe | 396 | uint32_t zebra_pbr_iptable_hash_key(const void *arg) |
7abd6c4f | 397 | { |
d8b87afe | 398 | const struct zebra_pbr_iptable *iptable = arg; |
7abd6c4f PG |
399 | uint32_t *pnt = (uint32_t *)&(iptable->ipset_name); |
400 | uint32_t key; | |
401 | ||
402 | key = jhash2(pnt, ZEBRA_IPSET_NAME_HASH_SIZE, | |
403 | 0x63ab42de); | |
404 | key = jhash_1word(iptable->fwmark, key); | |
a60b7031 PG |
405 | key = jhash_1word(iptable->family, key); |
406 | key = jhash_1word(iptable->flow_label, key); | |
e7f7dad4 PG |
407 | key = jhash_1word(iptable->pkt_len_min, key); |
408 | key = jhash_1word(iptable->pkt_len_max, key); | |
dc993e76 PG |
409 | key = jhash_1word(iptable->tcp_flags, key); |
410 | key = jhash_1word(iptable->tcp_mask_flags, key); | |
4977bd6c | 411 | key = jhash_1word(iptable->dscp_value, key); |
f449d223 | 412 | key = jhash_1word(iptable->protocol, key); |
5ac5b7cc | 413 | key = jhash_1word(iptable->fragment, key); |
62f20a52 DS |
414 | key = jhash_1word(iptable->vrf_id, key); |
415 | ||
7abd6c4f PG |
416 | return jhash_3words(iptable->filter_bm, iptable->type, |
417 | iptable->unique, key); | |
418 | } | |
419 | ||
74df8d6d | 420 | bool zebra_pbr_iptable_hash_equal(const void *arg1, const void *arg2) |
7abd6c4f PG |
421 | { |
422 | const struct zebra_pbr_iptable *r1, *r2; | |
423 | ||
424 | r1 = (const struct zebra_pbr_iptable *)arg1; | |
425 | r2 = (const struct zebra_pbr_iptable *)arg2; | |
426 | ||
62f20a52 | 427 | if (r1->vrf_id != r2->vrf_id) |
b08047f8 | 428 | return false; |
7abd6c4f | 429 | if (r1->type != r2->type) |
74df8d6d | 430 | return false; |
7abd6c4f | 431 | if (r1->unique != r2->unique) |
74df8d6d | 432 | return false; |
7abd6c4f | 433 | if (r1->filter_bm != r2->filter_bm) |
74df8d6d | 434 | return false; |
7abd6c4f | 435 | if (r1->fwmark != r2->fwmark) |
74df8d6d | 436 | return false; |
7abd6c4f | 437 | if (r1->action != r2->action) |
74df8d6d | 438 | return false; |
7abd6c4f PG |
439 | if (strncmp(r1->ipset_name, r2->ipset_name, |
440 | ZEBRA_IPSET_NAME_SIZE)) | |
74df8d6d | 441 | return false; |
a60b7031 PG |
442 | if (r1->family != r2->family) |
443 | return false; | |
444 | if (r1->flow_label != r2->flow_label) | |
445 | return false; | |
e7f7dad4 | 446 | if (r1->pkt_len_min != r2->pkt_len_min) |
74df8d6d | 447 | return false; |
e7f7dad4 | 448 | if (r1->pkt_len_max != r2->pkt_len_max) |
74df8d6d | 449 | return false; |
dc993e76 | 450 | if (r1->tcp_flags != r2->tcp_flags) |
74df8d6d | 451 | return false; |
dc993e76 | 452 | if (r1->tcp_mask_flags != r2->tcp_mask_flags) |
74df8d6d | 453 | return false; |
4977bd6c | 454 | if (r1->dscp_value != r2->dscp_value) |
74df8d6d | 455 | return false; |
5ac5b7cc | 456 | if (r1->fragment != r2->fragment) |
74df8d6d | 457 | return false; |
f449d223 PG |
458 | if (r1->protocol != r2->protocol) |
459 | return false; | |
74df8d6d | 460 | return true; |
7abd6c4f PG |
461 | } |
462 | ||
43fe6a2a DS |
463 | static void *pbr_rule_alloc_intern(void *arg) |
464 | { | |
465 | struct zebra_pbr_rule *zpr; | |
466 | struct zebra_pbr_rule *new; | |
467 | ||
468 | zpr = (struct zebra_pbr_rule *)arg; | |
469 | ||
470 | new = XCALLOC(MTYPE_TMP, sizeof(*new)); | |
471 | ||
472 | memcpy(new, zpr, sizeof(*zpr)); | |
473 | ||
474 | return new; | |
475 | } | |
476 | ||
3ae327cb SW |
477 | static int pbr_rule_release(struct zebra_pbr_rule *rule) |
478 | { | |
479 | struct zebra_pbr_rule *lookup; | |
480 | ||
481 | lookup = hash_lookup(zrouter.rules_hash, rule); | |
482 | ||
483 | if (!lookup) | |
484 | return -ENOENT; | |
485 | ||
486 | hash_release(zrouter.rules_hash, lookup); | |
487 | XFREE(MTYPE_TMP, lookup); | |
488 | ||
489 | return 0; | |
490 | } | |
491 | ||
7f0ea8a4 | 492 | void zebra_pbr_add_rule(struct zebra_pbr_rule *rule) |
43fe6a2a | 493 | { |
3ae327cb | 494 | struct zebra_pbr_rule *found; |
8c3cd6c6 | 495 | |
3ae327cb SW |
496 | /** |
497 | * Check if we already have it (this checks via a unique ID, walking | |
498 | * over the hash table, not via a hash operation). | |
8c3cd6c6 | 499 | */ |
3ae327cb SW |
500 | found = pbr_rule_lookup_unique(rule); |
501 | ||
502 | (void)hash_get(zrouter.rules_hash, rule, pbr_rule_alloc_intern); | |
503 | ||
504 | /* If found, this is an update */ | |
505 | if (found) { | |
f62e5480 | 506 | (void)dplane_pbr_rule_update(found, rule); |
3ae327cb SW |
507 | |
508 | if (pbr_rule_release(found)) | |
509 | zlog_debug( | |
510 | "%s: Rule being updated we know nothing about", | |
511 | __PRETTY_FUNCTION__); | |
512 | ||
513 | } else | |
f62e5480 | 514 | (void)dplane_pbr_rule_add(rule); |
1fbfe5a5 DS |
515 | } |
516 | ||
7f0ea8a4 | 517 | void zebra_pbr_del_rule(struct zebra_pbr_rule *rule) |
1fbfe5a5 | 518 | { |
f62e5480 | 519 | (void)dplane_pbr_rule_delete(rule); |
43fe6a2a | 520 | |
3ae327cb | 521 | if (pbr_rule_release(rule)) |
9df414fe | 522 | zlog_debug("%s: Rule being deleted we know nothing about", |
15569c58 | 523 | __func__); |
1fbfe5a5 DS |
524 | } |
525 | ||
e3b78da8 | 526 | static void zebra_pbr_cleanup_rules(struct hash_bucket *b, void *data) |
e69aa084 | 527 | { |
e69aa084 DS |
528 | struct zebra_pbr_rule *rule = b->data; |
529 | int *sock = data; | |
530 | ||
531 | if (rule->sock == *sock) { | |
f62e5480 | 532 | (void)dplane_pbr_rule_delete(rule); |
c4097b75 SW |
533 | if (hash_release(zrouter.rules_hash, rule)) |
534 | XFREE(MTYPE_TMP, rule); | |
535 | else | |
536 | zlog_debug( | |
537 | "%s: Rule seq: %u is being cleaned but we can't find it in our tables", | |
538 | __func__, rule->rule.seq); | |
e69aa084 DS |
539 | } |
540 | } | |
541 | ||
e3b78da8 | 542 | static void zebra_pbr_cleanup_ipset(struct hash_bucket *b, void *data) |
c2ef5232 | 543 | { |
c2ef5232 PG |
544 | struct zebra_pbr_ipset *ipset = b->data; |
545 | int *sock = data; | |
546 | ||
73a829f7 | 547 | if (ipset->sock == *sock) { |
b147e204 QY |
548 | if (hash_release(zrouter.ipset_hash, ipset)) |
549 | zebra_pbr_ipset_free(ipset); | |
550 | else | |
551 | hook_call(zebra_pbr_ipset_update, 0, ipset); | |
73a829f7 | 552 | } |
c2ef5232 PG |
553 | } |
554 | ||
e3b78da8 | 555 | static void zebra_pbr_cleanup_ipset_entry(struct hash_bucket *b, void *data) |
c2ef5232 | 556 | { |
c2ef5232 PG |
557 | struct zebra_pbr_ipset_entry *ipset = b->data; |
558 | int *sock = data; | |
559 | ||
73a829f7 | 560 | if (ipset->sock == *sock) { |
b147e204 QY |
561 | if (hash_release(zrouter.ipset_entry_hash, ipset)) |
562 | zebra_pbr_ipset_entry_free(ipset); | |
563 | else | |
564 | hook_call(zebra_pbr_ipset_entry_update, 0, ipset); | |
73a829f7 | 565 | } |
c2ef5232 PG |
566 | } |
567 | ||
e3b78da8 | 568 | static void zebra_pbr_cleanup_iptable(struct hash_bucket *b, void *data) |
c2ef5232 | 569 | { |
c2ef5232 PG |
570 | struct zebra_pbr_iptable *iptable = b->data; |
571 | int *sock = data; | |
572 | ||
73a829f7 | 573 | if (iptable->sock == *sock) { |
b147e204 | 574 | if (hash_release(zrouter.iptable_hash, iptable)) |
3b1de7b8 | 575 | _zebra_pbr_iptable_free_all(iptable, true); |
b147e204 QY |
576 | else |
577 | hook_call(zebra_pbr_iptable_update, 0, iptable); | |
73a829f7 | 578 | } |
c2ef5232 PG |
579 | } |
580 | ||
4c0ec639 | 581 | static int zebra_pbr_client_close_cleanup(struct zserv *client) |
e69aa084 | 582 | { |
4c0ec639 | 583 | int sock = client->sock; |
e69aa084 | 584 | |
4c0ec639 PG |
585 | if (!sock) |
586 | return 0; | |
7f0ea8a4 | 587 | hash_iterate(zrouter.rules_hash, zebra_pbr_cleanup_rules, &sock); |
62f20a52 DS |
588 | hash_iterate(zrouter.iptable_hash, zebra_pbr_cleanup_iptable, &sock); |
589 | hash_iterate(zrouter.ipset_entry_hash, zebra_pbr_cleanup_ipset_entry, | |
590 | &sock); | |
591 | hash_iterate(zrouter.ipset_hash, zebra_pbr_cleanup_ipset, &sock); | |
4c0ec639 PG |
592 | return 1; |
593 | } | |
594 | ||
595 | void zebra_pbr_init(void) | |
596 | { | |
c0ea1ae7 | 597 | hook_register(zserv_client_close, zebra_pbr_client_close_cleanup); |
e69aa084 DS |
598 | } |
599 | ||
7661461a PG |
600 | static void *pbr_ipset_alloc_intern(void *arg) |
601 | { | |
602 | struct zebra_pbr_ipset *zpi; | |
603 | struct zebra_pbr_ipset *new; | |
604 | ||
605 | zpi = (struct zebra_pbr_ipset *)arg; | |
606 | ||
607 | new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_ipset)); | |
608 | ||
609 | memcpy(new, zpi, sizeof(*zpi)); | |
610 | ||
611 | return new; | |
612 | } | |
613 | ||
62f20a52 | 614 | void zebra_pbr_create_ipset(struct zebra_pbr_ipset *ipset) |
7661461a | 615 | { |
73a829f7 PG |
616 | int ret; |
617 | ||
62f20a52 | 618 | (void)hash_get(zrouter.ipset_hash, ipset, pbr_ipset_alloc_intern); |
1c6fca1f | 619 | ret = hook_call(zebra_pbr_ipset_update, 1, ipset); |
73a829f7 | 620 | kernel_pbr_ipset_add_del_status(ipset, |
ea1c14f6 MS |
621 | ret ? ZEBRA_DPLANE_INSTALL_SUCCESS |
622 | : ZEBRA_DPLANE_INSTALL_FAILURE); | |
7661461a PG |
623 | } |
624 | ||
62f20a52 | 625 | void zebra_pbr_destroy_ipset(struct zebra_pbr_ipset *ipset) |
7661461a PG |
626 | { |
627 | struct zebra_pbr_ipset *lookup; | |
628 | ||
62f20a52 | 629 | lookup = hash_lookup(zrouter.ipset_hash, ipset); |
1c6fca1f | 630 | hook_call(zebra_pbr_ipset_update, 0, ipset); |
de67547d | 631 | if (lookup) { |
62f20a52 | 632 | hash_release(zrouter.ipset_hash, lookup); |
7661461a | 633 | XFREE(MTYPE_TMP, lookup); |
de67547d | 634 | } else |
9df414fe QY |
635 | zlog_debug( |
636 | "%s: IPSet Entry being deleted we know nothing about", | |
15569c58 | 637 | __func__); |
7661461a PG |
638 | } |
639 | ||
ed78b7c8 PG |
640 | struct pbr_ipset_name_lookup { |
641 | struct zebra_pbr_ipset *ipset; | |
642 | char ipset_name[ZEBRA_IPSET_NAME_SIZE]; | |
643 | }; | |
644 | ||
5b0d92b8 | 645 | const char *zebra_pbr_ipset_type2str(uint32_t type) |
586f4ccf PG |
646 | { |
647 | return lookup_msg(ipset_type_msg, type, | |
648 | "Unrecognized IPset Type"); | |
649 | } | |
650 | ||
e3b78da8 | 651 | static int zebra_pbr_ipset_pername_walkcb(struct hash_bucket *bucket, void *arg) |
ed78b7c8 PG |
652 | { |
653 | struct pbr_ipset_name_lookup *pinl = | |
654 | (struct pbr_ipset_name_lookup *)arg; | |
e3b78da8 | 655 | struct zebra_pbr_ipset *zpi = (struct zebra_pbr_ipset *)bucket->data; |
ed78b7c8 PG |
656 | |
657 | if (!strncmp(pinl->ipset_name, zpi->ipset_name, | |
658 | ZEBRA_IPSET_NAME_SIZE)) { | |
659 | pinl->ipset = zpi; | |
660 | return HASHWALK_ABORT; | |
661 | } | |
662 | return HASHWALK_CONTINUE; | |
663 | } | |
664 | ||
62f20a52 | 665 | struct zebra_pbr_ipset *zebra_pbr_lookup_ipset_pername(char *ipsetname) |
d59c13af | 666 | { |
ed78b7c8 PG |
667 | struct pbr_ipset_name_lookup pinl; |
668 | struct pbr_ipset_name_lookup *ptr = &pinl; | |
669 | ||
d59c13af PG |
670 | if (!ipsetname) |
671 | return NULL; | |
ed78b7c8 PG |
672 | memset(ptr, 0, sizeof(struct pbr_ipset_name_lookup)); |
673 | snprintf((char *)ptr->ipset_name, ZEBRA_IPSET_NAME_SIZE, "%s", | |
674 | ipsetname); | |
62f20a52 | 675 | hash_walk(zrouter.ipset_hash, zebra_pbr_ipset_pername_walkcb, ptr); |
ed78b7c8 | 676 | return ptr->ipset; |
d59c13af PG |
677 | } |
678 | ||
7661461a PG |
679 | static void *pbr_ipset_entry_alloc_intern(void *arg) |
680 | { | |
681 | struct zebra_pbr_ipset_entry *zpi; | |
682 | struct zebra_pbr_ipset_entry *new; | |
683 | ||
684 | zpi = (struct zebra_pbr_ipset_entry *)arg; | |
685 | ||
686 | new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_ipset_entry)); | |
687 | ||
688 | memcpy(new, zpi, sizeof(*zpi)); | |
689 | ||
690 | return new; | |
691 | } | |
692 | ||
62f20a52 | 693 | void zebra_pbr_add_ipset_entry(struct zebra_pbr_ipset_entry *ipset) |
7661461a | 694 | { |
73a829f7 PG |
695 | int ret; |
696 | ||
62f20a52 | 697 | (void)hash_get(zrouter.ipset_entry_hash, ipset, |
7661461a | 698 | pbr_ipset_entry_alloc_intern); |
1c6fca1f | 699 | ret = hook_call(zebra_pbr_ipset_entry_update, 1, ipset); |
73a829f7 | 700 | kernel_pbr_ipset_entry_add_del_status(ipset, |
ea1c14f6 MS |
701 | ret ? ZEBRA_DPLANE_INSTALL_SUCCESS |
702 | : ZEBRA_DPLANE_INSTALL_FAILURE); | |
7661461a PG |
703 | } |
704 | ||
62f20a52 | 705 | void zebra_pbr_del_ipset_entry(struct zebra_pbr_ipset_entry *ipset) |
7661461a PG |
706 | { |
707 | struct zebra_pbr_ipset_entry *lookup; | |
708 | ||
62f20a52 | 709 | lookup = hash_lookup(zrouter.ipset_entry_hash, ipset); |
1c6fca1f | 710 | hook_call(zebra_pbr_ipset_entry_update, 0, ipset); |
de67547d | 711 | if (lookup) { |
62f20a52 | 712 | hash_release(zrouter.ipset_entry_hash, lookup); |
7661461a | 713 | XFREE(MTYPE_TMP, lookup); |
de67547d | 714 | } else |
9df414fe | 715 | zlog_debug("%s: IPSet being deleted we know nothing about", |
15569c58 | 716 | __func__); |
7661461a PG |
717 | } |
718 | ||
7abd6c4f PG |
719 | static void *pbr_iptable_alloc_intern(void *arg) |
720 | { | |
721 | struct zebra_pbr_iptable *zpi; | |
722 | struct zebra_pbr_iptable *new; | |
592af4cc QY |
723 | struct listnode *ln; |
724 | char *ifname; | |
7abd6c4f PG |
725 | |
726 | zpi = (struct zebra_pbr_iptable *)arg; | |
727 | ||
728 | new = XCALLOC(MTYPE_TMP, sizeof(struct zebra_pbr_iptable)); | |
729 | ||
592af4cc | 730 | /* Deep structure copy */ |
7abd6c4f | 731 | memcpy(new, zpi, sizeof(*zpi)); |
592af4cc QY |
732 | new->interface_name_list = list_new(); |
733 | ||
734 | if (zpi->interface_name_list) { | |
735 | for (ALL_LIST_ELEMENTS_RO(zpi->interface_name_list, ln, ifname)) | |
736 | listnode_add(new->interface_name_list, | |
737 | XSTRDUP(MTYPE_PBR_IPTABLE_IFNAME, ifname)); | |
738 | } | |
7abd6c4f PG |
739 | |
740 | return new; | |
741 | } | |
742 | ||
62f20a52 | 743 | void zebra_pbr_add_iptable(struct zebra_pbr_iptable *iptable) |
7abd6c4f | 744 | { |
73a829f7 PG |
745 | int ret; |
746 | ||
62f20a52 | 747 | (void)hash_get(zrouter.iptable_hash, iptable, pbr_iptable_alloc_intern); |
1c6fca1f | 748 | ret = hook_call(zebra_pbr_iptable_update, 1, iptable); |
73a829f7 | 749 | kernel_pbr_iptable_add_del_status(iptable, |
ea1c14f6 MS |
750 | ret ? ZEBRA_DPLANE_INSTALL_SUCCESS |
751 | : ZEBRA_DPLANE_INSTALL_FAILURE); | |
7abd6c4f PG |
752 | } |
753 | ||
62f20a52 | 754 | void zebra_pbr_del_iptable(struct zebra_pbr_iptable *iptable) |
7abd6c4f | 755 | { |
f80ec7e3 | 756 | struct zebra_pbr_iptable *lookup; |
7abd6c4f | 757 | |
62f20a52 | 758 | lookup = hash_lookup(zrouter.iptable_hash, iptable); |
1c6fca1f | 759 | hook_call(zebra_pbr_iptable_update, 0, iptable); |
f80ec7e3 PG |
760 | if (lookup) { |
761 | struct listnode *node, *nnode; | |
762 | char *name; | |
763 | ||
62f20a52 | 764 | hash_release(zrouter.iptable_hash, lookup); |
f80ec7e3 PG |
765 | for (ALL_LIST_ELEMENTS(iptable->interface_name_list, |
766 | node, nnode, name)) { | |
767 | XFREE(MTYPE_PBR_IPTABLE_IFNAME, name); | |
768 | list_delete_node(iptable->interface_name_list, | |
769 | node); | |
770 | } | |
8b5c4dce | 771 | list_delete(&iptable->interface_name_list); |
7abd6c4f | 772 | XFREE(MTYPE_TMP, lookup); |
f80ec7e3 | 773 | } else |
9df414fe | 774 | zlog_debug("%s: IPTable being deleted we know nothing about", |
5e81f5dd | 775 | __func__); |
7abd6c4f PG |
776 | } |
777 | ||
942bf97b | 778 | /* |
779 | * Handle success or failure of rule (un)install in the kernel. | |
780 | */ | |
f62e5480 JU |
781 | void zebra_pbr_dplane_result(struct zebra_dplane_ctx *ctx) |
782 | { | |
783 | enum zebra_dplane_result res; | |
784 | enum dplane_op_e op; | |
785 | ||
786 | res = dplane_ctx_get_status(ctx); | |
787 | op = dplane_ctx_get_op(ctx); | |
788 | if (op == DPLANE_OP_RULE_ADD || op == DPLANE_OP_RULE_UPDATE) | |
789 | zsend_rule_notify_owner(ctx, res == ZEBRA_DPLANE_REQUEST_SUCCESS | |
790 | ? ZAPI_RULE_INSTALLED | |
791 | : ZAPI_RULE_FAIL_INSTALL); | |
792 | else if (op == DPLANE_OP_RULE_DELETE) | |
793 | zsend_rule_notify_owner(ctx, res == ZEBRA_DPLANE_REQUEST_SUCCESS | |
794 | ? ZAPI_RULE_REMOVED | |
795 | : ZAPI_RULE_FAIL_REMOVE); | |
796 | else | |
797 | flog_err( | |
798 | EC_ZEBRA_PBR_RULE_UPDATE, | |
799 | "Context received in pbr rule dplane result handler with incorrect OP code (%u)", | |
800 | op); | |
801 | ||
802 | ||
803 | dplane_ctx_fini(&ctx); | |
942bf97b | 804 | } |
805 | ||
425bdd6b PG |
806 | /* |
807 | * Handle success or failure of ipset (un)install in the kernel. | |
808 | */ | |
809 | void kernel_pbr_ipset_add_del_status(struct zebra_pbr_ipset *ipset, | |
ea1c14f6 | 810 | enum zebra_dplane_status res) |
425bdd6b PG |
811 | { |
812 | switch (res) { | |
ea1c14f6 | 813 | case ZEBRA_DPLANE_INSTALL_SUCCESS: |
425bdd6b PG |
814 | zsend_ipset_notify_owner(ipset, ZAPI_IPSET_INSTALLED); |
815 | break; | |
ea1c14f6 | 816 | case ZEBRA_DPLANE_INSTALL_FAILURE: |
425bdd6b PG |
817 | zsend_ipset_notify_owner(ipset, ZAPI_IPSET_FAIL_INSTALL); |
818 | break; | |
ea1c14f6 | 819 | case ZEBRA_DPLANE_DELETE_SUCCESS: |
4c550bcf PG |
820 | zsend_ipset_notify_owner(ipset, ZAPI_IPSET_REMOVED); |
821 | break; | |
ea1c14f6 | 822 | case ZEBRA_DPLANE_DELETE_FAILURE: |
34d9d5be | 823 | zsend_ipset_notify_owner(ipset, ZAPI_IPSET_FAIL_REMOVE); |
425bdd6b | 824 | break; |
ea1c14f6 MS |
825 | case ZEBRA_DPLANE_STATUS_NONE: |
826 | break; | |
425bdd6b PG |
827 | } |
828 | } | |
829 | ||
830 | /* | |
831 | * Handle success or failure of ipset (un)install in the kernel. | |
832 | */ | |
833 | void kernel_pbr_ipset_entry_add_del_status( | |
834 | struct zebra_pbr_ipset_entry *ipset, | |
ea1c14f6 | 835 | enum zebra_dplane_status res) |
425bdd6b PG |
836 | { |
837 | switch (res) { | |
ea1c14f6 | 838 | case ZEBRA_DPLANE_INSTALL_SUCCESS: |
425bdd6b PG |
839 | zsend_ipset_entry_notify_owner(ipset, |
840 | ZAPI_IPSET_ENTRY_INSTALLED); | |
841 | break; | |
ea1c14f6 | 842 | case ZEBRA_DPLANE_INSTALL_FAILURE: |
425bdd6b PG |
843 | zsend_ipset_entry_notify_owner(ipset, |
844 | ZAPI_IPSET_ENTRY_FAIL_INSTALL); | |
845 | break; | |
ea1c14f6 | 846 | case ZEBRA_DPLANE_DELETE_SUCCESS: |
4c550bcf PG |
847 | zsend_ipset_entry_notify_owner(ipset, |
848 | ZAPI_IPSET_ENTRY_REMOVED); | |
849 | break; | |
ea1c14f6 | 850 | case ZEBRA_DPLANE_DELETE_FAILURE: |
4c550bcf | 851 | zsend_ipset_entry_notify_owner(ipset, |
34d9d5be | 852 | ZAPI_IPSET_ENTRY_FAIL_REMOVE); |
425bdd6b | 853 | break; |
ea1c14f6 MS |
854 | case ZEBRA_DPLANE_STATUS_NONE: |
855 | break; | |
425bdd6b PG |
856 | } |
857 | } | |
858 | ||
7abd6c4f PG |
859 | /* |
860 | * Handle success or failure of ipset (un)install in the kernel. | |
861 | */ | |
862 | void kernel_pbr_iptable_add_del_status(struct zebra_pbr_iptable *iptable, | |
ea1c14f6 | 863 | enum zebra_dplane_status res) |
7abd6c4f PG |
864 | { |
865 | switch (res) { | |
ea1c14f6 | 866 | case ZEBRA_DPLANE_INSTALL_SUCCESS: |
7abd6c4f PG |
867 | zsend_iptable_notify_owner(iptable, ZAPI_IPTABLE_INSTALLED); |
868 | break; | |
ea1c14f6 | 869 | case ZEBRA_DPLANE_INSTALL_FAILURE: |
7abd6c4f PG |
870 | zsend_iptable_notify_owner(iptable, ZAPI_IPTABLE_FAIL_INSTALL); |
871 | break; | |
ea1c14f6 | 872 | case ZEBRA_DPLANE_DELETE_SUCCESS: |
4c550bcf PG |
873 | zsend_iptable_notify_owner(iptable, |
874 | ZAPI_IPTABLE_REMOVED); | |
875 | break; | |
ea1c14f6 | 876 | case ZEBRA_DPLANE_DELETE_FAILURE: |
4c550bcf | 877 | zsend_iptable_notify_owner(iptable, |
34d9d5be | 878 | ZAPI_IPTABLE_FAIL_REMOVE); |
7abd6c4f | 879 | break; |
ea1c14f6 MS |
880 | case ZEBRA_DPLANE_STATUS_NONE: |
881 | break; | |
7abd6c4f PG |
882 | } |
883 | } | |
884 | ||
942bf97b | 885 | /* |
886 | * Handle rule delete notification from kernel. | |
887 | */ | |
a0321978 | 888 | int kernel_pbr_rule_del(struct zebra_pbr_rule *rule) |
942bf97b | 889 | { |
890 | return 0; | |
891 | } | |
586f4ccf PG |
892 | |
893 | struct zebra_pbr_ipset_entry_unique_display { | |
894 | struct zebra_pbr_ipset *zpi; | |
895 | struct vty *vty; | |
73a829f7 | 896 | struct zebra_ns *zns; |
586f4ccf PG |
897 | }; |
898 | ||
899 | struct zebra_pbr_env_display { | |
900 | struct zebra_ns *zns; | |
901 | struct vty *vty; | |
7929821a | 902 | char *name; |
586f4ccf PG |
903 | }; |
904 | ||
905 | static const char *zebra_pbr_prefix2str(union prefixconstptr pu, | |
906 | char *str, int size) | |
907 | { | |
908 | const struct prefix *p = pu.p; | |
909 | char buf[PREFIX2STR_BUFFER]; | |
910 | ||
a60b7031 PG |
911 | if ((p->family == AF_INET && p->prefixlen == IPV4_MAX_PREFIXLEN) || |
912 | (p->family == AF_INET6 && p->prefixlen == IPV6_MAX_PREFIXLEN)) { | |
586f4ccf PG |
913 | snprintf(str, size, "%s", inet_ntop(p->family, &p->u.prefix, |
914 | buf, PREFIX2STR_BUFFER)); | |
915 | return str; | |
916 | } | |
917 | return prefix2str(pu, str, size); | |
918 | } | |
919 | ||
be729dd7 PG |
920 | static void zebra_pbr_display_icmp(struct vty *vty, |
921 | struct zebra_pbr_ipset_entry *zpie) | |
922 | { | |
923 | char decoded_str[20]; | |
924 | uint16_t port; | |
c9b1139a PG |
925 | struct zebra_pbr_ipset *zpi; |
926 | ||
927 | zpi = zpie->backpointer; | |
be729dd7 PG |
928 | |
929 | /* range icmp type */ | |
930 | if (zpie->src_port_max || zpie->dst_port_max) { | |
2b7165e7 | 931 | vty_out(vty, ":icmp:[type <%u:%u>;code <%u:%u>", |
be729dd7 PG |
932 | zpie->src_port_min, zpie->src_port_max, |
933 | zpie->dst_port_min, zpie->dst_port_max); | |
934 | } else { | |
935 | port = ((zpie->src_port_min << 8) & 0xff00) + | |
936 | (zpie->dst_port_min & 0xff); | |
937 | memset(decoded_str, 0, sizeof(decoded_str)); | |
2b7165e7 | 938 | snprintf(decoded_str, sizeof(decoded_str), "%u/%u", |
772270f3 | 939 | zpie->src_port_min, zpie->dst_port_min); |
c9b1139a PG |
940 | vty_out(vty, ":%s:%s", |
941 | zpi->family == AF_INET6 ? "ipv6-icmp" : "icmp", | |
942 | lookup_msg(zpi->family == AF_INET6 ? | |
943 | icmpv6_typecode_str : icmp_typecode_str, | |
be729dd7 PG |
944 | port, decoded_str)); |
945 | } | |
946 | } | |
947 | ||
25d760c5 PG |
948 | static void zebra_pbr_display_port(struct vty *vty, uint32_t filter_bm, |
949 | uint16_t port_min, uint16_t port_max, | |
950 | uint8_t proto) | |
951 | { | |
952 | if (!(filter_bm & PBR_FILTER_PROTO)) { | |
953 | if (port_max) | |
954 | vty_out(vty, ":udp/tcp:%d-%d", | |
955 | port_min, port_max); | |
956 | else | |
957 | vty_out(vty, ":udp/tcp:%d", | |
958 | port_min); | |
959 | } else { | |
960 | if (port_max) | |
961 | vty_out(vty, ":proto %d:%d-%d", | |
962 | proto, port_min, port_max); | |
963 | else | |
964 | vty_out(vty, ":proto %d:%d", | |
965 | proto, port_min); | |
966 | } | |
967 | } | |
968 | ||
e3b78da8 | 969 | static int zebra_pbr_show_ipset_entry_walkcb(struct hash_bucket *bucket, |
586f4ccf PG |
970 | void *arg) |
971 | { | |
972 | struct zebra_pbr_ipset_entry_unique_display *unique = | |
973 | (struct zebra_pbr_ipset_entry_unique_display *)arg; | |
974 | struct zebra_pbr_ipset *zpi = unique->zpi; | |
975 | struct vty *vty = unique->vty; | |
976 | struct zebra_pbr_ipset_entry *zpie = | |
e3b78da8 | 977 | (struct zebra_pbr_ipset_entry *)bucket->data; |
73a829f7 | 978 | uint64_t pkts = 0, bytes = 0; |
73a829f7 | 979 | int ret = 0; |
586f4ccf PG |
980 | |
981 | if (zpie->backpointer != zpi) | |
982 | return HASHWALK_CONTINUE; | |
983 | ||
25d760c5 PG |
984 | if ((zpi->type == IPSET_NET_NET) || |
985 | (zpi->type == IPSET_NET_PORT_NET)) { | |
586f4ccf PG |
986 | char buf[PREFIX_STRLEN]; |
987 | ||
988 | zebra_pbr_prefix2str(&(zpie->src), buf, sizeof(buf)); | |
989 | vty_out(vty, "\tfrom %s", buf); | |
be729dd7 PG |
990 | if (zpie->filter_bm & PBR_FILTER_SRC_PORT && |
991 | zpie->proto != IPPROTO_ICMP) | |
25d760c5 PG |
992 | zebra_pbr_display_port(vty, zpie->filter_bm, |
993 | zpie->src_port_min, | |
994 | zpie->src_port_max, | |
995 | zpie->proto); | |
586f4ccf PG |
996 | vty_out(vty, " to "); |
997 | zebra_pbr_prefix2str(&(zpie->dst), buf, sizeof(buf)); | |
998 | vty_out(vty, "%s", buf); | |
be729dd7 PG |
999 | if (zpie->filter_bm & PBR_FILTER_DST_PORT && |
1000 | zpie->proto != IPPROTO_ICMP) | |
25d760c5 PG |
1001 | zebra_pbr_display_port(vty, zpie->filter_bm, |
1002 | zpie->dst_port_min, | |
1003 | zpie->dst_port_max, | |
1004 | zpie->proto); | |
be729dd7 PG |
1005 | if (zpie->proto == IPPROTO_ICMP) |
1006 | zebra_pbr_display_icmp(vty, zpie); | |
25d760c5 PG |
1007 | } else if ((zpi->type == IPSET_NET) || |
1008 | (zpi->type == IPSET_NET_PORT)) { | |
586f4ccf PG |
1009 | char buf[PREFIX_STRLEN]; |
1010 | ||
1011 | if (zpie->filter_bm & PBR_FILTER_SRC_IP) { | |
1012 | zebra_pbr_prefix2str(&(zpie->src), buf, sizeof(buf)); | |
1013 | vty_out(vty, "\tfrom %s", buf); | |
1014 | } | |
be729dd7 PG |
1015 | if (zpie->filter_bm & PBR_FILTER_SRC_PORT && |
1016 | zpie->proto != IPPROTO_ICMP) | |
25d760c5 PG |
1017 | zebra_pbr_display_port(vty, zpie->filter_bm, |
1018 | zpie->src_port_min, | |
1019 | zpie->src_port_max, | |
1020 | zpie->proto); | |
586f4ccf PG |
1021 | if (zpie->filter_bm & PBR_FILTER_DST_IP) { |
1022 | zebra_pbr_prefix2str(&(zpie->dst), buf, sizeof(buf)); | |
1023 | vty_out(vty, "\tto %s", buf); | |
1024 | } | |
be729dd7 PG |
1025 | if (zpie->filter_bm & PBR_FILTER_DST_PORT && |
1026 | zpie->proto != IPPROTO_ICMP) | |
25d760c5 PG |
1027 | zebra_pbr_display_port(vty, zpie->filter_bm, |
1028 | zpie->dst_port_min, | |
1029 | zpie->dst_port_max, | |
1030 | zpie->proto); | |
be729dd7 PG |
1031 | if (zpie->proto == IPPROTO_ICMP) |
1032 | zebra_pbr_display_icmp(vty, zpie); | |
586f4ccf PG |
1033 | } |
1034 | vty_out(vty, " (%u)\n", zpie->unique); | |
1035 | ||
1c6fca1f | 1036 | ret = hook_call(zebra_pbr_ipset_entry_get_stat, zpie, &pkts, |
62f20a52 | 1037 | &bytes); |
73a829f7 PG |
1038 | if (ret && pkts > 0) |
1039 | vty_out(vty, "\t pkts %" PRIu64 ", bytes %" PRIu64"\n", | |
1040 | pkts, bytes); | |
586f4ccf PG |
1041 | return HASHWALK_CONTINUE; |
1042 | } | |
1043 | ||
e3b78da8 | 1044 | static int zebra_pbr_show_ipset_walkcb(struct hash_bucket *bucket, void *arg) |
586f4ccf PG |
1045 | { |
1046 | struct zebra_pbr_env_display *uniqueipset = | |
1047 | (struct zebra_pbr_env_display *)arg; | |
e3b78da8 | 1048 | struct zebra_pbr_ipset *zpi = (struct zebra_pbr_ipset *)bucket->data; |
586f4ccf PG |
1049 | struct zebra_pbr_ipset_entry_unique_display unique; |
1050 | struct vty *vty = uniqueipset->vty; | |
1051 | struct zebra_ns *zns = uniqueipset->zns; | |
1052 | ||
a60b7031 PG |
1053 | vty_out(vty, "IPset %s type %s family %s\n", zpi->ipset_name, |
1054 | zebra_pbr_ipset_type2str(zpi->type), | |
c6423c31 | 1055 | family2str(zpi->family)); |
586f4ccf PG |
1056 | unique.vty = vty; |
1057 | unique.zpi = zpi; | |
73a829f7 | 1058 | unique.zns = zns; |
62f20a52 | 1059 | hash_walk(zrouter.ipset_entry_hash, zebra_pbr_show_ipset_entry_walkcb, |
586f4ccf PG |
1060 | &unique); |
1061 | vty_out(vty, "\n"); | |
1062 | return HASHWALK_CONTINUE; | |
1063 | } | |
1064 | ||
dc993e76 PG |
1065 | size_t zebra_pbr_tcpflags_snprintf(char *buffer, size_t len, |
1066 | uint16_t tcp_val) | |
1067 | { | |
1068 | size_t len_written = 0; | |
1069 | static struct message nt = {0}; | |
1070 | const struct message *pnt; | |
1071 | int incr = 0; | |
1072 | ||
1073 | for (pnt = tcp_value_str; | |
1074 | memcmp(pnt, &nt, sizeof(struct message)); pnt++) | |
1075 | if (pnt->key & tcp_val) { | |
1076 | len_written += snprintf(buffer + len_written, | |
1077 | len - len_written, | |
1078 | "%s%s", incr ? | |
1079 | ",":"", pnt->str); | |
1080 | incr++; | |
1081 | } | |
1082 | return len_written; | |
1083 | } | |
1084 | ||
586f4ccf PG |
1085 | /* |
1086 | */ | |
1087 | void zebra_pbr_show_ipset_list(struct vty *vty, char *ipsetname) | |
1088 | { | |
1089 | struct zebra_pbr_ipset *zpi; | |
1090 | struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); | |
1091 | struct zebra_pbr_ipset_entry_unique_display unique; | |
1092 | struct zebra_pbr_env_display uniqueipset; | |
1093 | ||
1094 | if (ipsetname) { | |
62f20a52 | 1095 | zpi = zebra_pbr_lookup_ipset_pername(ipsetname); |
586f4ccf PG |
1096 | if (!zpi) { |
1097 | vty_out(vty, "No IPset %s found\n", ipsetname); | |
1098 | return; | |
1099 | } | |
a60b7031 PG |
1100 | vty_out(vty, "IPset %s type %s family %s\n", ipsetname, |
1101 | zebra_pbr_ipset_type2str(zpi->type), | |
c6423c31 | 1102 | family2str(zpi->family)); |
586f4ccf PG |
1103 | unique.vty = vty; |
1104 | unique.zpi = zpi; | |
73a829f7 | 1105 | unique.zns = zns; |
62f20a52 DS |
1106 | hash_walk(zrouter.ipset_entry_hash, |
1107 | zebra_pbr_show_ipset_entry_walkcb, &unique); | |
586f4ccf PG |
1108 | return; |
1109 | } | |
1110 | uniqueipset.zns = zns; | |
1111 | uniqueipset.vty = vty; | |
7929821a | 1112 | uniqueipset.name = NULL; |
62f20a52 | 1113 | hash_walk(zrouter.ipset_hash, zebra_pbr_show_ipset_walkcb, |
586f4ccf PG |
1114 | &uniqueipset); |
1115 | } | |
1116 | ||
1117 | struct pbr_rule_fwmark_lookup { | |
1118 | struct zebra_pbr_rule *ptr; | |
1119 | uint32_t fwmark; | |
1120 | }; | |
1121 | ||
e3b78da8 | 1122 | static int zebra_pbr_rule_lookup_fwmark_walkcb(struct hash_bucket *bucket, |
586f4ccf PG |
1123 | void *arg) |
1124 | { | |
1125 | struct pbr_rule_fwmark_lookup *iprule = | |
1126 | (struct pbr_rule_fwmark_lookup *)arg; | |
e3b78da8 | 1127 | struct zebra_pbr_rule *zpr = (struct zebra_pbr_rule *)bucket->data; |
586f4ccf PG |
1128 | |
1129 | if (iprule->fwmark == zpr->rule.filter.fwmark) { | |
1130 | iprule->ptr = zpr; | |
1131 | return HASHWALK_ABORT; | |
1132 | } | |
1133 | return HASHWALK_CONTINUE; | |
1134 | } | |
1135 | ||
7929821a PG |
1136 | static void zebra_pbr_show_iptable_unit(struct zebra_pbr_iptable *iptable, |
1137 | struct vty *vty, | |
1138 | struct zebra_ns *zns) | |
586f4ccf | 1139 | { |
73a829f7 PG |
1140 | int ret; |
1141 | uint64_t pkts = 0, bytes = 0; | |
586f4ccf | 1142 | |
a60b7031 PG |
1143 | vty_out(vty, "IPtable %s family %s action %s (%u)\n", |
1144 | iptable->ipset_name, | |
c9b1139a | 1145 | family2str(iptable->family), |
586f4ccf PG |
1146 | iptable->action == ZEBRA_IPTABLES_DROP ? "drop" : "redirect", |
1147 | iptable->unique); | |
0b328d3f PG |
1148 | if (iptable->type == IPSET_NET_PORT || |
1149 | iptable->type == IPSET_NET_PORT_NET) { | |
1150 | if (!(iptable->filter_bm & MATCH_ICMP_SET)) { | |
1151 | if (iptable->filter_bm & PBR_FILTER_DST_PORT) | |
1152 | vty_out(vty, "\t lookup dst port\n"); | |
1153 | else if (iptable->filter_bm & PBR_FILTER_SRC_PORT) | |
1154 | vty_out(vty, "\t lookup src port\n"); | |
1155 | } | |
1156 | } | |
e7f7dad4 PG |
1157 | if (iptable->pkt_len_min || iptable->pkt_len_max) { |
1158 | if (!iptable->pkt_len_max) | |
1159 | vty_out(vty, "\t pkt len %u\n", | |
1160 | iptable->pkt_len_min); | |
1161 | else | |
1162 | vty_out(vty, "\t pkt len [%u;%u]\n", | |
1163 | iptable->pkt_len_min, | |
1164 | iptable->pkt_len_max); | |
1165 | } | |
dc993e76 PG |
1166 | if (iptable->tcp_flags || iptable->tcp_mask_flags) { |
1167 | char tcp_flag_str[64]; | |
1168 | char tcp_flag_mask_str[64]; | |
1169 | ||
1170 | zebra_pbr_tcpflags_snprintf(tcp_flag_str, | |
1171 | sizeof(tcp_flag_str), | |
1172 | iptable->tcp_flags); | |
1173 | zebra_pbr_tcpflags_snprintf(tcp_flag_mask_str, | |
1174 | sizeof(tcp_flag_mask_str), | |
1175 | iptable->tcp_mask_flags); | |
1176 | vty_out(vty, "\t tcpflags [%s/%s]\n", | |
1177 | tcp_flag_str, tcp_flag_mask_str); | |
1178 | } | |
69214c57 PG |
1179 | if (iptable->filter_bm & (MATCH_DSCP_SET | MATCH_DSCP_INVERSE_SET)) { |
1180 | vty_out(vty, "\t dscp %s %d\n", | |
1181 | iptable->filter_bm & MATCH_DSCP_INVERSE_SET ? | |
1182 | "not" : "", iptable->dscp_value); | |
1183 | } | |
a60b7031 PG |
1184 | if (iptable->filter_bm & (MATCH_FLOW_LABEL_SET | |
1185 | MATCH_FLOW_LABEL_INVERSE_SET)) { | |
1186 | vty_out(vty, "\t flowlabel %s %d\n", | |
1187 | iptable->filter_bm & MATCH_FLOW_LABEL_INVERSE_SET ? | |
1188 | "not" : "", iptable->flow_label); | |
1189 | } | |
5ac5b7cc PG |
1190 | if (iptable->fragment) { |
1191 | char val_str[10]; | |
1192 | ||
772270f3 | 1193 | snprintf(val_str, sizeof(val_str), "%d", iptable->fragment); |
5ac5b7cc PG |
1194 | vty_out(vty, "\t fragment%s %s\n", |
1195 | iptable->filter_bm & MATCH_FRAGMENT_INVERSE_SET ? | |
1196 | " not" : "", lookup_msg(fragment_value_str, | |
1197 | iptable->fragment, val_str)); | |
1198 | } | |
f449d223 PG |
1199 | if (iptable->protocol) { |
1200 | vty_out(vty, "\t protocol %d\n", | |
1201 | iptable->protocol); | |
1202 | } | |
1c6fca1f | 1203 | ret = hook_call(zebra_pbr_iptable_get_stat, iptable, &pkts, |
62f20a52 | 1204 | &bytes); |
73a829f7 PG |
1205 | if (ret && pkts > 0) |
1206 | vty_out(vty, "\t pkts %" PRIu64 ", bytes %" PRIu64"\n", | |
1207 | pkts, bytes); | |
586f4ccf PG |
1208 | if (iptable->action != ZEBRA_IPTABLES_DROP) { |
1209 | struct pbr_rule_fwmark_lookup prfl; | |
1210 | ||
1211 | prfl.fwmark = iptable->fwmark; | |
1212 | prfl.ptr = NULL; | |
7f0ea8a4 | 1213 | hash_walk(zrouter.rules_hash, |
586f4ccf PG |
1214 | &zebra_pbr_rule_lookup_fwmark_walkcb, &prfl); |
1215 | if (prfl.ptr) { | |
1216 | struct zebra_pbr_rule *zpr = prfl.ptr; | |
1217 | ||
1218 | vty_out(vty, "\t table %u, fwmark %u\n", | |
1219 | zpr->rule.action.table, | |
1220 | prfl.fwmark); | |
1221 | } | |
1222 | } | |
7929821a PG |
1223 | } |
1224 | ||
e3b78da8 | 1225 | static int zebra_pbr_show_iptable_walkcb(struct hash_bucket *bucket, void *arg) |
7929821a PG |
1226 | { |
1227 | struct zebra_pbr_iptable *iptable = | |
e3b78da8 | 1228 | (struct zebra_pbr_iptable *)bucket->data; |
7929821a PG |
1229 | struct zebra_pbr_env_display *env = (struct zebra_pbr_env_display *)arg; |
1230 | struct vty *vty = env->vty; | |
1231 | struct zebra_ns *zns = env->zns; | |
1232 | char *iptable_name = env->name; | |
1233 | ||
1234 | if (!iptable_name) | |
1235 | zebra_pbr_show_iptable_unit(iptable, vty, zns); | |
1236 | else if (!strncmp(iptable_name, | |
1237 | iptable->ipset_name, | |
1238 | ZEBRA_IPSET_NAME_SIZE)) | |
1239 | zebra_pbr_show_iptable_unit(iptable, vty, zns); | |
586f4ccf PG |
1240 | return HASHWALK_CONTINUE; |
1241 | } | |
1242 | ||
7929821a | 1243 | void zebra_pbr_show_iptable(struct vty *vty, char *iptable_name) |
586f4ccf PG |
1244 | { |
1245 | struct zebra_ns *zns = zebra_ns_lookup(NS_DEFAULT); | |
1246 | struct zebra_pbr_env_display env; | |
1247 | ||
1248 | env.vty = vty; | |
1249 | env.zns = zns; | |
7929821a | 1250 | env.name = iptable_name; |
62f20a52 | 1251 | hash_walk(zrouter.iptable_hash, zebra_pbr_show_iptable_walkcb, &env); |
586f4ccf | 1252 | } |
f80ec7e3 PG |
1253 | |
1254 | void zebra_pbr_iptable_update_interfacelist(struct stream *s, | |
1255 | struct zebra_pbr_iptable *zpi) | |
1256 | { | |
1257 | uint32_t i = 0, index; | |
1258 | struct interface *ifp; | |
1259 | char *name; | |
1260 | ||
1261 | for (i = 0; i < zpi->nb_interface; i++) { | |
1262 | STREAM_GETL(s, index); | |
1263 | ifp = if_lookup_by_index(index, zpi->vrf_id); | |
1264 | if (!ifp) | |
1265 | continue; | |
1266 | name = XSTRDUP(MTYPE_PBR_IPTABLE_IFNAME, ifp->name); | |
1267 | listnode_add(zpi->interface_name_list, name); | |
1268 | } | |
1269 | stream_failure: | |
1270 | return; | |
1271 | } |