xref: /aosp_15_r20/external/iptables/iptables/nft-ruleparse.c (revision a71a954618bbadd4a345637e5edcf36eec826889)
1 /*
2  * (C) 2012-2013 by Pablo Neira Ayuso <[email protected]>
3  * (C) 2013 by Tomasz Bursztyka <[email protected]>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
11  */
12 
13 #include <stdbool.h>
14 #include <stdlib.h>
15 #include <string.h>
16 
17 #include <linux/netfilter/nf_log.h>
18 #include <linux/netfilter/xt_limit.h>
19 #include <linux/netfilter/xt_mark.h>
20 #include <linux/netfilter/xt_NFLOG.h>
21 #include <linux/netfilter/xt_pkttype.h>
22 
23 #include <linux/netfilter_ipv6/ip6t_hl.h>
24 
25 #include <libnftnl/rule.h>
26 #include <libnftnl/expr.h>
27 
28 #include <xtables.h>
29 
30 #include "nft-ruleparse.h"
31 #include "nft.h"
32 
33 static struct xtables_match *
nft_find_match_in_cs(struct iptables_command_state * cs,const char * name)34 nft_find_match_in_cs(struct iptables_command_state *cs, const char *name)
35 {
36 	struct xtables_rule_match *rm;
37 	struct ebt_match *ebm;
38 
39 	for (ebm = cs->match_list; ebm; ebm = ebm->next) {
40 		if (ebm->ismatch &&
41 		    !strcmp(ebm->u.match->m->u.user.name, name))
42 			return ebm->u.match;
43 	}
44 	for (rm = cs->matches; rm; rm = rm->next) {
45 		if (!strcmp(rm->match->m->u.user.name, name))
46 			return rm->match;
47 	}
48 	return NULL;
49 }
50 
51 void *
nft_create_match(struct nft_xt_ctx * ctx,struct iptables_command_state * cs,const char * name,bool reuse)52 nft_create_match(struct nft_xt_ctx *ctx,
53 		 struct iptables_command_state *cs,
54 		 const char *name, bool reuse)
55 {
56 	struct xtables_match *match;
57 	struct xt_entry_match *m;
58 	unsigned int size;
59 
60 	if (reuse) {
61 		match = nft_find_match_in_cs(cs, name);
62 		if (match)
63 			return match->m->data;
64 	}
65 
66 	match = xtables_find_match(name, XTF_TRY_LOAD,
67 				   &cs->matches);
68 	if (!match)
69 		return NULL;
70 
71 	size = XT_ALIGN(sizeof(struct xt_entry_match)) + match->size;
72 	m = xtables_calloc(1, size);
73 	m->u.match_size = size;
74 	m->u.user.revision = match->revision;
75 
76 	strcpy(m->u.user.name, match->name);
77 	match->m = m;
78 
79 	xs_init_match(match);
80 
81 	if (ctx->h->ops->rule_parse->match)
82 		ctx->h->ops->rule_parse->match(match, cs);
83 
84 	return match->m->data;
85 }
86 
87 static void *
__nft_create_target(struct nft_xt_ctx * ctx,const char * name,size_t tgsize)88 __nft_create_target(struct nft_xt_ctx *ctx, const char *name, size_t tgsize)
89 {
90 	struct xtables_target *target;
91 	size_t size;
92 
93 	target = xtables_find_target(name, XTF_TRY_LOAD);
94 	if (!target)
95 		return NULL;
96 
97 	size = XT_ALIGN(sizeof(*target->t)) + tgsize ?: target->size;
98 
99 	target->t = xtables_calloc(1, size);
100 	target->t->u.target_size = size;
101 	target->t->u.user.revision = target->revision;
102 	strcpy(target->t->u.user.name, name);
103 
104 	xs_init_target(target);
105 
106 	ctx->cs->jumpto = name;
107 	ctx->cs->target = target;
108 
109 	if (ctx->h->ops->rule_parse->target)
110 		ctx->h->ops->rule_parse->target(target, ctx->cs);
111 
112 	return target->t->data;
113 }
114 
115 void *
nft_create_target(struct nft_xt_ctx * ctx,const char * name)116 nft_create_target(struct nft_xt_ctx *ctx, const char *name)
117 {
118 	return __nft_create_target(ctx, name, 0);
119 }
120 
nft_parse_counter(struct nftnl_expr * e,struct xt_counters * counters)121 static void nft_parse_counter(struct nftnl_expr *e, struct xt_counters *counters)
122 {
123 	counters->pcnt = nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_PACKETS);
124 	counters->bcnt = nftnl_expr_get_u64(e, NFTNL_EXPR_CTR_BYTES);
125 }
126 
nft_parse_payload(struct nft_xt_ctx * ctx,struct nftnl_expr * e)127 static void nft_parse_payload(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
128 {
129 	enum nft_registers regnum = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_DREG);
130 	struct nft_xt_ctx_reg *reg = nft_xt_ctx_get_dreg(ctx, regnum);
131 
132 	if (!reg)
133 		return;
134 
135 	reg->type = NFT_XT_REG_PAYLOAD;
136 	reg->payload.base = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_BASE);
137 	reg->payload.offset = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET);
138 	reg->payload.len = nftnl_expr_get_u32(e, NFTNL_EXPR_PAYLOAD_LEN);
139 }
140 
nft_parse_meta_set_common(struct nft_xt_ctx * ctx,struct nft_xt_ctx_reg * sreg)141 static bool nft_parse_meta_set_common(struct nft_xt_ctx* ctx,
142 				      struct nft_xt_ctx_reg *sreg)
143 {
144 	if ((sreg->type != NFT_XT_REG_IMMEDIATE)) {
145 		ctx->errmsg = "meta sreg is not an immediate";
146 		return false;
147 	}
148 
149 	return true;
150 }
151 
nft_parse_meta_set(struct nft_xt_ctx * ctx,struct nftnl_expr * e)152 static void nft_parse_meta_set(struct nft_xt_ctx *ctx,
153 			       struct nftnl_expr *e)
154 {
155 	struct nft_xt_ctx_reg *sreg;
156 	enum nft_registers sregnum;
157 
158 	sregnum = nftnl_expr_get_u32(e, NFTNL_EXPR_META_SREG);
159 	sreg = nft_xt_ctx_get_sreg(ctx, sregnum);
160 	if (!sreg)
161 		return;
162 
163 	switch (nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY)) {
164 	case NFT_META_NFTRACE:
165 		if (!nft_parse_meta_set_common(ctx, sreg))
166 			return;
167 
168 		if (sreg->immediate.data[0] == 0) {
169 			ctx->errmsg = "meta sreg immediate is 0";
170 			return;
171 		}
172 
173 		if (!nft_create_target(ctx, "TRACE"))
174 			ctx->errmsg = "target TRACE not found";
175 		break;
176 	case NFT_META_BRI_BROUTE:
177 		if (!nft_parse_meta_set_common(ctx, sreg))
178 			return;
179 
180 		ctx->cs->jumpto = "DROP";
181 		break;
182 	case NFT_META_MARK: {
183 		struct xt_mark_tginfo2 *mt;
184 
185 		if (!nft_parse_meta_set_common(ctx, sreg))
186 			return;
187 
188 		mt = nft_create_target(ctx, "MARK");
189 		if (!mt) {
190 			ctx->errmsg = "target MARK not found";
191 			return;
192 		}
193 
194 		mt->mark = sreg->immediate.data[0];
195 		if (sreg->bitwise.set)
196 			mt->mask = sreg->bitwise.mask[0];
197 		else
198 			mt->mask = ~0u;
199 		break;
200 	}
201 	default:
202 		ctx->errmsg = "meta sreg key not supported";
203 		break;
204 	}
205 }
206 
nft_parse_meta(struct nft_xt_ctx * ctx,struct nftnl_expr * e)207 static void nft_parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
208 {
209         struct nft_xt_ctx_reg *reg;
210 
211 	if (nftnl_expr_is_set(e, NFTNL_EXPR_META_SREG)) {
212 		nft_parse_meta_set(ctx, e);
213 		return;
214 	}
215 
216 	reg = nft_xt_ctx_get_dreg(ctx, nftnl_expr_get_u32(e, NFTNL_EXPR_META_DREG));
217 	if (!reg)
218 		return;
219 
220 	reg->meta_dreg.key = nftnl_expr_get_u32(e, NFTNL_EXPR_META_KEY);
221 	reg->type = NFT_XT_REG_META_DREG;
222 }
223 
nft_parse_bitwise(struct nft_xt_ctx * ctx,struct nftnl_expr * e)224 static void nft_parse_bitwise(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
225 {
226 	enum nft_registers sregnum = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_SREG);
227 	enum nft_registers dregnum = nftnl_expr_get_u32(e, NFTNL_EXPR_BITWISE_DREG);
228 	struct nft_xt_ctx_reg *sreg = nft_xt_ctx_get_sreg(ctx, sregnum);
229 	struct nft_xt_ctx_reg *dreg = sreg;
230 	const void *data;
231 	uint32_t len;
232 
233 	if (!sreg)
234 		return;
235 
236 	if (sregnum != dregnum) {
237 		dreg = nft_xt_ctx_get_sreg(ctx, dregnum); /* sreg, do NOT clear ... */
238 		if (!dreg)
239 			return;
240 
241 		*dreg = *sreg;  /* .. and copy content instead */
242 	}
243 
244 	data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_XOR, &len);
245 
246 	if (len > sizeof(dreg->bitwise.xor)) {
247 		ctx->errmsg = "bitwise xor too large";
248 		return;
249 	}
250 
251 	memcpy(dreg->bitwise.xor, data, len);
252 
253 	data = nftnl_expr_get(e, NFTNL_EXPR_BITWISE_MASK, &len);
254 
255 	if (len > sizeof(dreg->bitwise.mask)) {
256 		ctx->errmsg = "bitwise mask too large";
257 		return;
258 	}
259 
260 	memcpy(dreg->bitwise.mask, data, len);
261 
262 	dreg->bitwise.set = true;
263 }
264 
nft_parse_icmp(struct nft_xt_ctx * ctx,struct iptables_command_state * cs,struct nft_xt_ctx_reg * sreg,uint8_t op,const char * data,size_t dlen)265 static void nft_parse_icmp(struct nft_xt_ctx *ctx,
266 			   struct iptables_command_state *cs,
267 			   struct nft_xt_ctx_reg *sreg,
268 			   uint8_t op, const char *data, size_t dlen)
269 {
270 	struct ipt_icmp icmp = {
271 		.type = UINT8_MAX,
272 		.code = { 0, UINT8_MAX },
273 	}, *icmpp;
274 
275 	if (dlen < 1)
276 		goto out_err_len;
277 
278 	switch (sreg->payload.offset) {
279 	case 0:
280 		icmp.type = data[0];
281 		if (dlen == 1)
282 			break;
283 		dlen--;
284 		data++;
285 		/* fall through */
286 	case 1:
287 		if (dlen > 1)
288 			goto out_err_len;
289 		icmp.code[0] = icmp.code[1] = data[0];
290 		break;
291 	default:
292 		ctx->errmsg = "unexpected payload offset";
293 		return;
294 	}
295 
296 	switch (ctx->h->family) {
297 	case NFPROTO_IPV4:
298 		icmpp = nft_create_match(ctx, cs, "icmp", false);
299 		break;
300 	case NFPROTO_IPV6:
301 		if (icmp.type == UINT8_MAX) {
302 			ctx->errmsg = "icmp6 code with any type match not supported";
303 			return;
304 		}
305 		icmpp = nft_create_match(ctx, cs, "icmp6", false);
306 		break;
307 	default:
308 		ctx->errmsg = "unexpected family for icmp match";
309 		return;
310 	}
311 
312 	if (!icmpp) {
313 		ctx->errmsg = "icmp match extension not found";
314 		return;
315 	}
316 	memcpy(icmpp, &icmp, sizeof(icmp));
317 	return;
318 
319 out_err_len:
320 	ctx->errmsg = "unexpected RHS data length";
321 }
322 
port_match_single_to_range(__u16 * ports,__u8 * invflags,uint8_t op,int port,__u8 invflag)323 static void port_match_single_to_range(__u16 *ports, __u8 *invflags,
324 				       uint8_t op, int port, __u8 invflag)
325 {
326 	if (port < 0)
327 		return;
328 
329 	switch (op) {
330 	case NFT_CMP_NEQ:
331 		*invflags |= invflag;
332 		/* fallthrough */
333 	case NFT_CMP_EQ:
334 		ports[0] = port;
335 		ports[1] = port;
336 		break;
337 	case NFT_CMP_LT:
338 		ports[1] = max(port - 1, 1);
339 		break;
340 	case NFT_CMP_LTE:
341 		ports[1] = port;
342 		break;
343 	case NFT_CMP_GT:
344 		ports[0] = min(port + 1, UINT16_MAX);
345 		break;
346 	case NFT_CMP_GTE:
347 		ports[0] = port;
348 		break;
349 	}
350 }
351 
nft_parse_udp(struct nft_xt_ctx * ctx,struct iptables_command_state * cs,int sport,int dport,uint8_t op)352 static void nft_parse_udp(struct nft_xt_ctx *ctx,
353 			  struct iptables_command_state *cs,
354 			  int sport, int dport,
355 			  uint8_t op)
356 {
357 	struct xt_udp *udp = nft_create_match(ctx, cs, "udp", true);
358 
359 	if (!udp) {
360 		ctx->errmsg = "udp match extension not found";
361 		return;
362 	}
363 
364 	port_match_single_to_range(udp->spts, &udp->invflags,
365 				   op, sport, XT_UDP_INV_SRCPT);
366 	port_match_single_to_range(udp->dpts, &udp->invflags,
367 				   op, dport, XT_UDP_INV_DSTPT);
368 }
369 
nft_parse_tcp(struct nft_xt_ctx * ctx,struct iptables_command_state * cs,int sport,int dport,uint8_t op)370 static void nft_parse_tcp(struct nft_xt_ctx *ctx,
371 			  struct iptables_command_state *cs,
372 			  int sport, int dport,
373 			  uint8_t op)
374 {
375 	struct xt_tcp *tcp = nft_create_match(ctx, cs, "tcp", true);
376 
377 	if (!tcp) {
378 		ctx->errmsg = "tcp match extension not found";
379 		return;
380 	}
381 
382 	port_match_single_to_range(tcp->spts, &tcp->invflags,
383 				   op, sport, XT_TCP_INV_SRCPT);
384 	port_match_single_to_range(tcp->dpts, &tcp->invflags,
385 				   op, dport, XT_TCP_INV_DSTPT);
386 }
387 
nft_parse_th_port(struct nft_xt_ctx * ctx,struct iptables_command_state * cs,uint8_t proto,int sport,int dport,uint8_t op)388 static void nft_parse_th_port(struct nft_xt_ctx *ctx,
389 			      struct iptables_command_state *cs,
390 			      uint8_t proto,
391 			      int sport, int dport, uint8_t op)
392 {
393 	switch (proto) {
394 	case IPPROTO_UDP:
395 		nft_parse_udp(ctx, cs, sport, dport, op);
396 		break;
397 	case IPPROTO_TCP:
398 		nft_parse_tcp(ctx, cs, sport, dport, op);
399 		break;
400 	default:
401 		ctx->errmsg = "unknown layer 4 protocol for TH match";
402 	}
403 }
404 
nft_parse_tcp_flags(struct nft_xt_ctx * ctx,struct iptables_command_state * cs,uint8_t op,uint8_t flags,uint8_t mask)405 static void nft_parse_tcp_flags(struct nft_xt_ctx *ctx,
406 				struct iptables_command_state *cs,
407 				uint8_t op, uint8_t flags, uint8_t mask)
408 {
409 	struct xt_tcp *tcp = nft_create_match(ctx, cs, "tcp", true);
410 
411 	if (!tcp) {
412 		ctx->errmsg = "tcp match extension not found";
413 		return;
414 	}
415 
416 	if (op == NFT_CMP_NEQ)
417 		tcp->invflags |= XT_TCP_INV_FLAGS;
418 	tcp->flg_cmp = flags;
419 	tcp->flg_mask = mask;
420 }
421 
nft_parse_transport(struct nft_xt_ctx * ctx,struct nftnl_expr * e,struct iptables_command_state * cs)422 static void nft_parse_transport(struct nft_xt_ctx *ctx,
423 				struct nftnl_expr *e,
424 				struct iptables_command_state *cs)
425 {
426 	struct nft_xt_ctx_reg *sreg;
427 	enum nft_registers reg;
428 	uint32_t sdport;
429 	uint16_t port;
430 	uint8_t proto, op;
431 	unsigned int len;
432 
433 	switch (ctx->h->family) {
434 	case NFPROTO_IPV4:
435 		proto = ctx->cs->fw.ip.proto;
436 		break;
437 	case NFPROTO_IPV6:
438 		proto = ctx->cs->fw6.ipv6.proto;
439 		break;
440 	default:
441 		ctx->errmsg = "invalid family for TH match";
442 		return;
443 	}
444 
445 	nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
446 	op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
447 
448 	reg = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG);
449 	sreg = nft_xt_ctx_get_sreg(ctx, reg);
450 	if (!sreg)
451 		return;
452 
453 	if (sreg->type != NFT_XT_REG_PAYLOAD) {
454 		ctx->errmsg = "sgreg not payload";
455 		return;
456 	}
457 
458 	switch (proto) {
459 	case IPPROTO_UDP:
460 	case IPPROTO_TCP:
461 		break;
462 	case IPPROTO_ICMP:
463 	case IPPROTO_ICMPV6:
464 		nft_parse_icmp(ctx, cs, sreg, op,
465 			       nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len),
466 			       len);
467 		return;
468 	default:
469 		ctx->errmsg = "unsupported layer 4 protocol value";
470 		return;
471 	}
472 
473 	switch(sreg->payload.offset) {
474 	case 0: /* th->sport */
475 		switch (len) {
476 		case 2: /* load sport only */
477 			port = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_CMP_DATA));
478 			nft_parse_th_port(ctx, cs, proto, port, -1, op);
479 			return;
480 		case 4: /* load both src and dst port */
481 			sdport = ntohl(nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA));
482 			nft_parse_th_port(ctx, cs, proto, sdport >> 16, sdport & 0xffff, op);
483 			return;
484 		}
485 		break;
486 	case 2: /* th->dport */
487 		switch (len) {
488 		case 2:
489 			port = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_CMP_DATA));
490 			nft_parse_th_port(ctx, cs, proto, -1, port, op);
491 			return;
492 		}
493 		break;
494 	case 13: /* th->flags */
495 		if (len == 1 && proto == IPPROTO_TCP) {
496 			uint8_t flags = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
497 			uint8_t mask = ~0;
498 
499 			if (sreg->bitwise.set)
500 				memcpy(&mask, &sreg->bitwise.mask, sizeof(mask));
501 
502 			nft_parse_tcp_flags(ctx, cs, op, flags, mask);
503 		}
504 		return;
505 	}
506 }
507 
nft_parse_cmp(struct nft_xt_ctx * ctx,struct nftnl_expr * e)508 static void nft_parse_cmp(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
509 {
510 	struct nft_xt_ctx_reg *sreg;
511 	uint32_t reg;
512 
513 	reg = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG);
514 
515 	sreg = nft_xt_ctx_get_sreg(ctx, reg);
516 	if (!sreg)
517 		return;
518 
519 	switch (sreg->type) {
520 	case NFT_XT_REG_UNDEF:
521 		ctx->errmsg = "cmp sreg undef";
522 		break;
523 	case NFT_XT_REG_META_DREG:
524 		ctx->h->ops->rule_parse->meta(ctx, sreg, e, ctx->cs);
525 		break;
526 	case NFT_XT_REG_PAYLOAD:
527 		switch (sreg->payload.base) {
528 		case NFT_PAYLOAD_LL_HEADER:
529 			if (ctx->h->family == NFPROTO_BRIDGE)
530 				ctx->h->ops->rule_parse->payload(ctx, sreg, e, ctx->cs);
531 			break;
532 		case NFT_PAYLOAD_NETWORK_HEADER:
533 			ctx->h->ops->rule_parse->payload(ctx, sreg, e, ctx->cs);
534 			break;
535 		case NFT_PAYLOAD_TRANSPORT_HEADER:
536 			nft_parse_transport(ctx, e, ctx->cs);
537 			break;
538 		}
539 
540 		break;
541 	default:
542 		ctx->errmsg = "cmp sreg has unknown type";
543 		break;
544 	}
545 }
546 
nft_parse_immediate(struct nft_xt_ctx * ctx,struct nftnl_expr * e)547 static void nft_parse_immediate(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
548 {
549 	const char *chain = nftnl_expr_get_str(e, NFTNL_EXPR_IMM_CHAIN);
550 	struct iptables_command_state *cs = ctx->cs;
551 	int verdict;
552 
553 	if (nftnl_expr_is_set(e, NFTNL_EXPR_IMM_DATA)) {
554 		struct nft_xt_ctx_reg *dreg;
555 		const void *imm_data;
556 		uint32_t len;
557 
558 		imm_data = nftnl_expr_get(e, NFTNL_EXPR_IMM_DATA, &len);
559 		dreg = nft_xt_ctx_get_dreg(ctx, nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_DREG));
560 		if (!dreg)
561 			return;
562 
563 		if (len > sizeof(dreg->immediate.data)) {
564 			ctx->errmsg = "oversized immediate data";
565 			return;
566 		}
567 
568 		memcpy(dreg->immediate.data, imm_data, len);
569 		dreg->immediate.len = len;
570 		dreg->type = NFT_XT_REG_IMMEDIATE;
571 
572 		return;
573 	}
574 
575 	verdict = nftnl_expr_get_u32(e, NFTNL_EXPR_IMM_VERDICT);
576 	/* Standard target? */
577 	switch(verdict) {
578 	case NF_ACCEPT:
579 		if (cs->jumpto && strcmp(ctx->table, "broute") == 0)
580 			break;
581 		cs->jumpto = "ACCEPT";
582 		break;
583 	case NF_DROP:
584 		cs->jumpto = "DROP";
585 		break;
586 	case NFT_RETURN:
587 		cs->jumpto = "RETURN";
588 		break;;
589 	case NFT_GOTO:
590 		if (ctx->h->ops->set_goto_flag)
591 			ctx->h->ops->set_goto_flag(cs);
592 		/* fall through */
593 	case NFT_JUMP:
594 		cs->jumpto = chain;
595 		/* fall through */
596 	default:
597 		return;
598 	}
599 
600 	if (!nft_create_target(ctx, cs->jumpto))
601 		ctx->errmsg = "verdict extension not found";
602 }
603 
nft_parse_match(struct nft_xt_ctx * ctx,struct nftnl_expr * e)604 static void nft_parse_match(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
605 {
606 	uint32_t mt_len;
607 	const char *mt_name = nftnl_expr_get_str(e, NFTNL_EXPR_MT_NAME);
608 	const void *mt_info = nftnl_expr_get(e, NFTNL_EXPR_MT_INFO, &mt_len);
609 	struct xtables_match *match;
610 	struct xtables_rule_match **matches;
611 	struct xt_entry_match *m;
612 
613 	switch (ctx->h->family) {
614 	case NFPROTO_IPV4:
615 	case NFPROTO_IPV6:
616 	case NFPROTO_BRIDGE:
617 		matches = &ctx->cs->matches;
618 		break;
619 	default:
620 		fprintf(stderr, "BUG: nft_parse_match() unknown family %d\n",
621 			ctx->h->family);
622 		exit(EXIT_FAILURE);
623 	}
624 
625 	match = xtables_find_match(mt_name, XTF_TRY_LOAD, matches);
626 	if (match == NULL) {
627 		ctx->errmsg = "match extension not found";
628 		return;
629 	}
630 
631 	m = xtables_calloc(1, sizeof(struct xt_entry_match) + mt_len);
632 	memcpy(&m->data, mt_info, mt_len);
633 	m->u.match_size = mt_len + XT_ALIGN(sizeof(struct xt_entry_match));
634 	m->u.user.revision = nftnl_expr_get_u32(e, NFTNL_EXPR_TG_REV);
635 	strcpy(m->u.user.name, match->name);
636 
637 	match->m = m;
638 
639 	if (ctx->h->ops->rule_parse->match != NULL)
640 		ctx->h->ops->rule_parse->match(match, ctx->cs);
641 }
642 
nft_parse_target(struct nft_xt_ctx * ctx,struct nftnl_expr * e)643 static void nft_parse_target(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
644 {
645 	uint32_t tg_len;
646 	const char *targname = nftnl_expr_get_str(e, NFTNL_EXPR_TG_NAME);
647 	const void *targinfo = nftnl_expr_get(e, NFTNL_EXPR_TG_INFO, &tg_len);
648 	void *data;
649 
650 	data = __nft_create_target(ctx, targname, tg_len);
651 	if (!data)
652 		ctx->errmsg = "target extension not found";
653 	else
654 		memcpy(data, targinfo, tg_len);
655 }
656 
nft_parse_limit(struct nft_xt_ctx * ctx,struct nftnl_expr * e)657 static void nft_parse_limit(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
658 {
659 	__u32 burst = nftnl_expr_get_u32(e, NFTNL_EXPR_LIMIT_BURST);
660 	__u64 unit = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_UNIT);
661 	__u64 rate = nftnl_expr_get_u64(e, NFTNL_EXPR_LIMIT_RATE);
662 	struct xt_rateinfo *rinfo;
663 
664 	switch (ctx->h->family) {
665 	case NFPROTO_IPV4:
666 	case NFPROTO_IPV6:
667 	case NFPROTO_BRIDGE:
668 		break;
669 	default:
670 		fprintf(stderr, "BUG: nft_parse_limit() unknown family %d\n",
671 			ctx->h->family);
672 		exit(EXIT_FAILURE);
673 	}
674 
675 	rinfo = nft_create_match(ctx, ctx->cs, "limit", false);
676 	if (!rinfo) {
677 		ctx->errmsg = "limit match extension not found";
678 		return;
679 	}
680 
681 	rinfo->avg = XT_LIMIT_SCALE * unit / rate;
682 	rinfo->burst = burst;
683 }
684 
nft_parse_lookup(struct nft_xt_ctx * ctx,struct nft_handle * h,struct nftnl_expr * e)685 static void nft_parse_lookup(struct nft_xt_ctx *ctx, struct nft_handle *h,
686 			     struct nftnl_expr *e)
687 {
688 	if (ctx->h->ops->rule_parse->lookup)
689 		ctx->h->ops->rule_parse->lookup(ctx, e);
690 }
691 
nft_parse_log(struct nft_xt_ctx * ctx,struct nftnl_expr * e)692 static void nft_parse_log(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
693 {
694 	/*
695 	 * In order to handle the longer log-prefix supported by nft, instead of
696 	 * using struct xt_nflog_info, we use a struct with a compatible layout, but
697 	 * a larger buffer for the prefix.
698 	 */
699 	struct xt_nflog_info_nft {
700 		__u32 len;
701 		__u16 group;
702 		__u16 threshold;
703 		__u16 flags;
704 		__u16 pad;
705 		char  prefix[NF_LOG_PREFIXLEN];
706 	} info = {
707 		.group     = nftnl_expr_get_u16(e, NFTNL_EXPR_LOG_GROUP),
708 		.threshold = nftnl_expr_get_u16(e, NFTNL_EXPR_LOG_QTHRESHOLD),
709 	};
710 	void *data;
711 
712 	if (nftnl_expr_is_set(e, NFTNL_EXPR_LOG_SNAPLEN)) {
713 		info.len = nftnl_expr_get_u32(e, NFTNL_EXPR_LOG_SNAPLEN);
714 		info.flags = XT_NFLOG_F_COPY_LEN;
715 	}
716 	if (nftnl_expr_is_set(e, NFTNL_EXPR_LOG_PREFIX))
717 		snprintf(info.prefix, sizeof(info.prefix), "%s",
718 			 nftnl_expr_get_str(e, NFTNL_EXPR_LOG_PREFIX));
719 
720 	data = __nft_create_target(ctx, "NFLOG",
721 				   XT_ALIGN(sizeof(struct xt_nflog_info_nft)));
722 	if (!data)
723 		ctx->errmsg = "NFLOG target extension not found";
724 	else
725 		memcpy(data, &info, sizeof(info));
726 }
727 
nft_parse_udp_range(struct nft_xt_ctx * ctx,struct iptables_command_state * cs,int sport_from,int sport_to,int dport_from,int dport_to,uint8_t op)728 static void nft_parse_udp_range(struct nft_xt_ctx *ctx,
729 			        struct iptables_command_state *cs,
730 			        int sport_from, int sport_to,
731 			        int dport_from, int dport_to,
732 				uint8_t op)
733 {
734 	struct xt_udp *udp = nft_create_match(ctx, cs, "udp", true);
735 
736 	if (!udp) {
737 		ctx->errmsg = "udp match extension not found";
738 		return;
739 	}
740 
741 	if (sport_from >= 0) {
742 		switch (op) {
743 		case NFT_RANGE_NEQ:
744 			udp->invflags |= XT_UDP_INV_SRCPT;
745 			/* fallthrough */
746 		case NFT_RANGE_EQ:
747 			udp->spts[0] = sport_from;
748 			udp->spts[1] = sport_to;
749 			break;
750 		}
751 	}
752 
753 	if (dport_to >= 0) {
754 		switch (op) {
755 		case NFT_CMP_NEQ:
756 			udp->invflags |= XT_UDP_INV_DSTPT;
757 			/* fallthrough */
758 		case NFT_CMP_EQ:
759 			udp->dpts[0] = dport_from;
760 			udp->dpts[1] = dport_to;
761 			break;
762 		}
763 	}
764 }
765 
nft_parse_tcp_range(struct nft_xt_ctx * ctx,struct iptables_command_state * cs,int sport_from,int sport_to,int dport_from,int dport_to,uint8_t op)766 static void nft_parse_tcp_range(struct nft_xt_ctx *ctx,
767 			        struct iptables_command_state *cs,
768 			        int sport_from, int sport_to,
769 			        int dport_from, int dport_to,
770 				uint8_t op)
771 {
772 	struct xt_tcp *tcp = nft_create_match(ctx, cs, "tcp", true);
773 
774 	if (!tcp) {
775 		ctx->errmsg = "tcp match extension not found";
776 		return;
777 	}
778 
779 	if (sport_from >= 0) {
780 		switch (op) {
781 		case NFT_RANGE_NEQ:
782 			tcp->invflags |= XT_TCP_INV_SRCPT;
783 			/* fallthrough */
784 		case NFT_RANGE_EQ:
785 			tcp->spts[0] = sport_from;
786 			tcp->spts[1] = sport_to;
787 			break;
788 		}
789 	}
790 
791 	if (dport_to >= 0) {
792 		switch (op) {
793 		case NFT_CMP_NEQ:
794 			tcp->invflags |= XT_TCP_INV_DSTPT;
795 			/* fallthrough */
796 		case NFT_CMP_EQ:
797 			tcp->dpts[0] = dport_from;
798 			tcp->dpts[1] = dport_to;
799 			break;
800 		}
801 	}
802 }
803 
nft_parse_th_port_range(struct nft_xt_ctx * ctx,struct iptables_command_state * cs,uint8_t proto,int sport_from,int sport_to,int dport_from,int dport_to,uint8_t op)804 static void nft_parse_th_port_range(struct nft_xt_ctx *ctx,
805 				    struct iptables_command_state *cs,
806 				    uint8_t proto,
807 				    int sport_from, int sport_to,
808 				    int dport_from, int dport_to, uint8_t op)
809 {
810 	switch (proto) {
811 	case IPPROTO_UDP:
812 		nft_parse_udp_range(ctx, cs, sport_from, sport_to, dport_from, dport_to, op);
813 		break;
814 	case IPPROTO_TCP:
815 		nft_parse_tcp_range(ctx, cs, sport_from, sport_to, dport_from, dport_to, op);
816 		break;
817 	}
818 }
819 
nft_parse_transport_range(struct nft_xt_ctx * ctx,const struct nft_xt_ctx_reg * sreg,struct nftnl_expr * e,struct iptables_command_state * cs)820 static void nft_parse_transport_range(struct nft_xt_ctx *ctx,
821 				      const struct nft_xt_ctx_reg *sreg,
822 				      struct nftnl_expr *e,
823 				      struct iptables_command_state *cs)
824 {
825 	unsigned int len_from, len_to;
826 	uint8_t proto, op;
827 	uint16_t from, to;
828 
829 	switch (ctx->h->family) {
830 	case NFPROTO_IPV4:
831 		proto = ctx->cs->fw.ip.proto;
832 		break;
833 	case NFPROTO_IPV6:
834 		proto = ctx->cs->fw6.ipv6.proto;
835 		break;
836 	default:
837 		proto = 0;
838 		break;
839 	}
840 
841 	nftnl_expr_get(e, NFTNL_EXPR_RANGE_FROM_DATA, &len_from);
842 	nftnl_expr_get(e, NFTNL_EXPR_RANGE_FROM_DATA, &len_to);
843 	if (len_to != len_from || len_to != 2)
844 		return;
845 
846 	op = nftnl_expr_get_u32(e, NFTNL_EXPR_RANGE_OP);
847 
848 	from = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_FROM_DATA));
849 	to = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_TO_DATA));
850 
851 	switch (sreg->payload.offset) {
852 	case 0:
853 		nft_parse_th_port_range(ctx, cs, proto, from, to, -1, -1, op);
854 		return;
855 	case 2:
856 		to = ntohs(nftnl_expr_get_u16(e, NFTNL_EXPR_RANGE_TO_DATA));
857 		nft_parse_th_port_range(ctx, cs, proto, -1, -1, from, to, op);
858 		return;
859 	}
860 }
861 
nft_parse_range(struct nft_xt_ctx * ctx,struct nftnl_expr * e)862 static void nft_parse_range(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
863 {
864 	struct nft_xt_ctx_reg *sreg;
865 	uint32_t reg;
866 
867 	reg = nftnl_expr_get_u32(e, NFTNL_EXPR_RANGE_SREG);
868 	sreg = nft_xt_ctx_get_sreg(ctx, reg);
869 
870 	switch (sreg->type) {
871 	case NFT_XT_REG_UNDEF:
872 		ctx->errmsg = "range sreg undef";
873 		break;
874 	case NFT_XT_REG_PAYLOAD:
875 		switch (sreg->payload.base) {
876 		case NFT_PAYLOAD_TRANSPORT_HEADER:
877 			nft_parse_transport_range(ctx, sreg, e, ctx->cs);
878 			break;
879 		default:
880 			ctx->errmsg = "range with unknown payload base";
881 			break;
882 		}
883 		break;
884 	default:
885 		ctx->errmsg = "range sreg type unsupported";
886 		break;
887 	}
888 }
889 
nft_rule_to_iptables_command_state(struct nft_handle * h,const struct nftnl_rule * r,struct iptables_command_state * cs)890 bool nft_rule_to_iptables_command_state(struct nft_handle *h,
891 					const struct nftnl_rule *r,
892 					struct iptables_command_state *cs)
893 {
894 	struct nftnl_expr_iter *iter;
895 	struct nftnl_expr *expr;
896 	struct nft_xt_ctx ctx = {
897 		.cs = cs,
898 		.h = h,
899 		.table = nftnl_rule_get_str(r, NFTNL_RULE_TABLE),
900 	};
901 	bool ret = true;
902 
903 	iter = nftnl_expr_iter_create(r);
904 	if (iter == NULL)
905 		return false;
906 
907 	ctx.iter = iter;
908 	expr = nftnl_expr_iter_next(iter);
909 	while (expr != NULL) {
910 		const char *name =
911 			nftnl_expr_get_str(expr, NFTNL_EXPR_NAME);
912 
913 		if (strcmp(name, "counter") == 0)
914 			nft_parse_counter(expr, &ctx.cs->counters);
915 		else if (strcmp(name, "payload") == 0)
916 			nft_parse_payload(&ctx, expr);
917 		else if (strcmp(name, "meta") == 0)
918 			nft_parse_meta(&ctx, expr);
919 		else if (strcmp(name, "bitwise") == 0)
920 			nft_parse_bitwise(&ctx, expr);
921 		else if (strcmp(name, "cmp") == 0)
922 			nft_parse_cmp(&ctx, expr);
923 		else if (strcmp(name, "immediate") == 0)
924 			nft_parse_immediate(&ctx, expr);
925 		else if (strcmp(name, "match") == 0)
926 			nft_parse_match(&ctx, expr);
927 		else if (strcmp(name, "target") == 0)
928 			nft_parse_target(&ctx, expr);
929 		else if (strcmp(name, "limit") == 0)
930 			nft_parse_limit(&ctx, expr);
931 		else if (strcmp(name, "lookup") == 0)
932 			nft_parse_lookup(&ctx, h, expr);
933 		else if (strcmp(name, "log") == 0)
934 			nft_parse_log(&ctx, expr);
935 		else if (strcmp(name, "range") == 0)
936 			nft_parse_range(&ctx, expr);
937 
938 		if (ctx.errmsg) {
939 			fprintf(stderr, "Error: %s\n", ctx.errmsg);
940 			ctx.errmsg = NULL;
941 			ret = false;
942 		}
943 
944 		expr = nftnl_expr_iter_next(iter);
945 	}
946 
947 	nftnl_expr_iter_destroy(iter);
948 
949 	if (nftnl_rule_is_set(r, NFTNL_RULE_USERDATA)) {
950 		const void *data;
951 		uint32_t len, size;
952 		const char *comment;
953 
954 		data = nftnl_rule_get_data(r, NFTNL_RULE_USERDATA, &len);
955 		comment = get_comment(data, len);
956 		if (comment) {
957 			struct xtables_match *match;
958 			struct xt_entry_match *m;
959 
960 			match = xtables_find_match("comment", XTF_TRY_LOAD,
961 						   &cs->matches);
962 			if (match == NULL)
963 				return false;
964 
965 			size = XT_ALIGN(sizeof(struct xt_entry_match))
966 				+ match->size;
967 			m = xtables_calloc(1, size);
968 
969 			strncpy((char *)m->data, comment, match->size - 1);
970 			m->u.match_size = size;
971 			m->u.user.revision = 0;
972 			strcpy(m->u.user.name, match->name);
973 
974 			match->m = m;
975 		}
976 	}
977 
978 	if (!cs->jumpto)
979 		cs->jumpto = "";
980 
981 	if (!ret)
982 		xtables_error(VERSION_PROBLEM, "Parsing nftables rule failed");
983 	return ret;
984 }
985 
parse_ifname(const char * name,unsigned int len,char * dst,unsigned char * mask)986 static void parse_ifname(const char *name, unsigned int len,
987 			 char *dst, unsigned char *mask)
988 {
989 	if (len == 0)
990 		return;
991 
992 	memcpy(dst, name, len);
993 	if (name[len - 1] == '\0') {
994 		if (mask)
995 			memset(mask, 0xff, strlen(name) + 1);
996 		return;
997 	}
998 
999 	if (len >= IFNAMSIZ)
1000 		return;
1001 
1002 	/* wildcard */
1003 	dst[len++] = '+';
1004 	if (len >= IFNAMSIZ)
1005 		return;
1006 	dst[len++] = 0;
1007 	if (mask)
1008 		memset(mask, 0xff, len - 2);
1009 }
1010 
parse_invalid_iface(char * iface,unsigned char * mask,uint8_t * invflags,uint8_t invbit)1011 static void parse_invalid_iface(char *iface, unsigned char *mask,
1012 				uint8_t *invflags, uint8_t invbit)
1013 {
1014 	if (*invflags & invbit || strcmp(iface, "INVAL/D"))
1015 		return;
1016 
1017 	/* nft's poor "! -o +" excuse */
1018 	*invflags |= invbit;
1019 	iface[0] = '+';
1020 	iface[1] = '\0';
1021 	mask[0] = 0xff;
1022 	mask[1] = 0xff;
1023 	memset(mask + 2, 0, IFNAMSIZ - 2);
1024 }
1025 
get_meta_mask(struct nft_xt_ctx * ctx,enum nft_registers sreg)1026 static uint32_t get_meta_mask(struct nft_xt_ctx *ctx, enum nft_registers sreg)
1027 {
1028 	struct nft_xt_ctx_reg *reg = nft_xt_ctx_get_sreg(ctx, sreg);
1029 
1030 	if (reg->bitwise.set)
1031 		return reg->bitwise.mask[0];
1032 
1033 	return ~0u;
1034 }
1035 
parse_meta_mark(struct nft_xt_ctx * ctx,struct nftnl_expr * e)1036 static int parse_meta_mark(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
1037 {
1038 	struct xt_mark_mtinfo1 *mark;
1039 	uint32_t value;
1040 
1041 	mark = nft_create_match(ctx, ctx->cs, "mark", false);
1042 	if (!mark)
1043 		return -1;
1044 
1045 	if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
1046 		mark->invert = 1;
1047 
1048 	value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
1049 	mark->mark = value;
1050 	mark->mask = get_meta_mask(ctx, nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_SREG));
1051 
1052 	return 0;
1053 }
1054 
parse_meta_pkttype(struct nft_xt_ctx * ctx,struct nftnl_expr * e)1055 static int parse_meta_pkttype(struct nft_xt_ctx *ctx, struct nftnl_expr *e)
1056 {
1057 	struct xt_pkttype_info *pkttype;
1058 	uint8_t value;
1059 
1060 	pkttype = nft_create_match(ctx, ctx->cs, "pkttype", false);
1061 	if (!pkttype)
1062 		return -1;
1063 
1064 	if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
1065 		pkttype->invert = 1;
1066 
1067 	value = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
1068 	pkttype->pkttype = value;
1069 
1070 	return 0;
1071 }
1072 
parse_meta(struct nft_xt_ctx * ctx,struct nftnl_expr * e,uint8_t key,char * iniface,unsigned char * iniface_mask,char * outiface,unsigned char * outiface_mask,uint8_t * invflags)1073 int parse_meta(struct nft_xt_ctx *ctx, struct nftnl_expr *e, uint8_t key,
1074 	       char *iniface, unsigned char *iniface_mask,
1075 	       char *outiface, unsigned char *outiface_mask, uint8_t *invflags)
1076 {
1077 	uint32_t value;
1078 	const void *ifname;
1079 	uint32_t len;
1080 
1081 	switch(key) {
1082 	case NFT_META_IIF:
1083 		value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
1084 		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
1085 			*invflags |= IPT_INV_VIA_IN;
1086 
1087 		if_indextoname(value, iniface);
1088 
1089 		memset(iniface_mask, 0xff, strlen(iniface)+1);
1090 		break;
1091 	case NFT_META_OIF:
1092 		value = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_DATA);
1093 		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
1094 			*invflags |= IPT_INV_VIA_OUT;
1095 
1096 		if_indextoname(value, outiface);
1097 
1098 		memset(outiface_mask, 0xff, strlen(outiface)+1);
1099 		break;
1100 	case NFT_META_BRI_IIFNAME:
1101 	case NFT_META_IIFNAME:
1102 		ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
1103 		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
1104 			*invflags |= IPT_INV_VIA_IN;
1105 
1106 		parse_ifname(ifname, len, iniface, iniface_mask);
1107 		parse_invalid_iface(iniface, iniface_mask,
1108 				    invflags, IPT_INV_VIA_IN);
1109 		break;
1110 	case NFT_META_BRI_OIFNAME:
1111 	case NFT_META_OIFNAME:
1112 		ifname = nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &len);
1113 		if (nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP) == NFT_CMP_NEQ)
1114 			*invflags |= IPT_INV_VIA_OUT;
1115 
1116 		parse_ifname(ifname, len, outiface, outiface_mask);
1117 		parse_invalid_iface(outiface, outiface_mask,
1118 				    invflags, IPT_INV_VIA_OUT);
1119 		break;
1120 	case NFT_META_MARK:
1121 		parse_meta_mark(ctx, e);
1122 		break;
1123 	case NFT_META_PKTTYPE:
1124 		parse_meta_pkttype(ctx, e);
1125 		break;
1126 	default:
1127 		return -1;
1128 	}
1129 
1130 	return 0;
1131 }
1132 
nft_parse_hl(struct nft_xt_ctx * ctx,struct nftnl_expr * e,struct iptables_command_state * cs)1133 int nft_parse_hl(struct nft_xt_ctx *ctx, struct nftnl_expr *e,
1134 		 struct iptables_command_state *cs)
1135 {
1136 	struct ip6t_hl_info *info;
1137 	uint8_t hl, mode;
1138 	int op;
1139 
1140 	hl = nftnl_expr_get_u8(e, NFTNL_EXPR_CMP_DATA);
1141 	op = nftnl_expr_get_u32(e, NFTNL_EXPR_CMP_OP);
1142 
1143 	switch (op) {
1144 	case NFT_CMP_NEQ:
1145 		mode = IP6T_HL_NE;
1146 		break;
1147 	case NFT_CMP_EQ:
1148 		mode = IP6T_HL_EQ;
1149 		break;
1150 	case NFT_CMP_LT:
1151 		mode = IP6T_HL_LT;
1152 		break;
1153 	case NFT_CMP_GT:
1154 		mode = IP6T_HL_GT;
1155 		break;
1156 	case NFT_CMP_LTE:
1157 		mode = IP6T_HL_LT;
1158 		if (hl == 255)
1159 			return -1;
1160 		hl++;
1161 		break;
1162 	case NFT_CMP_GTE:
1163 		mode = IP6T_HL_GT;
1164 		if (hl == 0)
1165 			return -1;
1166 		hl--;
1167 		break;
1168 	default:
1169 		return -1;
1170 	}
1171 
1172 	/* ipt_ttl_info and ip6t_hl_info have same layout,
1173 	 * IPT_TTL_x and IP6T_HL_x are aliases as well, so
1174 	 * just use HL for both ipv4 and ipv6.
1175 	 */
1176 	switch (ctx->h->family) {
1177 	case NFPROTO_IPV4:
1178 		info = nft_create_match(ctx, ctx->cs, "ttl", false);
1179 		break;
1180 	case NFPROTO_IPV6:
1181 		info = nft_create_match(ctx, ctx->cs, "hl", false);
1182 		break;
1183 	default:
1184 		return -1;
1185 	}
1186 
1187 	if (!info)
1188 		return -1;
1189 
1190 	info->hop_limit = hl;
1191 	info->mode = mode;
1192 
1193 	return 0;
1194 }
1195