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