xref: /aosp_15_r20/external/iptables/iptables/nft-ruleparse-arp.c (revision a71a954618bbadd4a345637e5edcf36eec826889)
1 /*
2  * (C) 2013 by Pablo Neira Ayuso <[email protected]>
3  * (C) 2013 by Giuseppe Longo <[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 <stddef.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <netdb.h>
18 #include <net/if.h>
19 #include <net/if_arp.h>
20 #include <netinet/if_ether.h>
21 
22 #include <libnftnl/rule.h>
23 #include <libnftnl/expr.h>
24 
25 #include "nft-shared.h"
26 #include "nft-ruleparse.h"
27 #include "xshared.h"
28 
nft_arp_parse_meta(struct nft_xt_ctx * ctx,const struct nft_xt_ctx_reg * reg,struct nftnl_expr * e,struct iptables_command_state * cs)29 static void nft_arp_parse_meta(struct nft_xt_ctx *ctx,
30 			       const struct nft_xt_ctx_reg *reg,
31 			       struct nftnl_expr *e,
32 			       struct iptables_command_state *cs)
33 {
34 	struct arpt_entry *fw = &cs->arp;
35 	uint8_t flags = 0;
36 
37 	if (parse_meta(ctx, e, reg->meta_dreg.key, fw->arp.iniface, fw->arp.iniface_mask,
38 		   fw->arp.outiface, fw->arp.outiface_mask,
39 		   &flags) == 0) {
40 		fw->arp.invflags |= flags;
41 		return;
42 	}
43 
44 	ctx->errmsg = "Unknown arp meta key";
45 }
46 
parse_mask_ipv4(const struct nft_xt_ctx_reg * reg,struct in_addr * mask)47 static void parse_mask_ipv4(const struct nft_xt_ctx_reg *reg, struct in_addr *mask)
48 {
49 	mask->s_addr = reg->bitwise.mask[0];
50 }
51 
nft_arp_parse_devaddr(const struct nft_xt_ctx_reg * reg,struct nftnl_expr * e,struct arpt_devaddr_info * info)52 static bool nft_arp_parse_devaddr(const struct nft_xt_ctx_reg *reg,
53 				  struct nftnl_expr *e,
54 				  struct arpt_devaddr_info *info)
55 {
56 	uint32_t hlen;
57 	bool inv;
58 
59 	nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &hlen);
60 
61 	if (hlen != ETH_ALEN)
62 		return false;
63 
64 	get_cmp_data(e, info->addr, ETH_ALEN, &inv);
65 
66 	if (reg->bitwise.set)
67 		memcpy(info->mask, reg->bitwise.mask, ETH_ALEN);
68 	else
69 		memset(info->mask, 0xff,
70 		       min(reg->payload.len, ETH_ALEN));
71 
72 	return inv;
73 }
74 
nft_arp_parse_payload(struct nft_xt_ctx * ctx,const struct nft_xt_ctx_reg * reg,struct nftnl_expr * e,struct iptables_command_state * cs)75 static void nft_arp_parse_payload(struct nft_xt_ctx *ctx,
76 				  const struct nft_xt_ctx_reg *reg,
77 				  struct nftnl_expr *e,
78 				  struct iptables_command_state *cs)
79 {
80 	struct arpt_entry *fw = &cs->arp;
81 	struct in_addr addr;
82 	uint16_t ar_hrd, ar_pro, ar_op;
83 	uint8_t ar_hln, ar_pln;
84 	bool inv;
85 
86 	switch (reg->payload.offset) {
87 	case offsetof(struct arphdr, ar_hrd):
88 		get_cmp_data(e, &ar_hrd, sizeof(ar_hrd), &inv);
89 		fw->arp.arhrd = ar_hrd;
90 		fw->arp.arhrd_mask = 0xffff;
91 		if (inv)
92 			fw->arp.invflags |= IPT_INV_ARPHRD;
93 		break;
94 	case offsetof(struct arphdr, ar_pro):
95 		get_cmp_data(e, &ar_pro, sizeof(ar_pro), &inv);
96 		fw->arp.arpro = ar_pro;
97 		fw->arp.arpro_mask = 0xffff;
98 		if (inv)
99 			fw->arp.invflags |= IPT_INV_PROTO;
100 		break;
101 	case offsetof(struct arphdr, ar_op):
102 		get_cmp_data(e, &ar_op, sizeof(ar_op), &inv);
103 		fw->arp.arpop = ar_op;
104 		fw->arp.arpop_mask = 0xffff;
105 		if (inv)
106 			fw->arp.invflags |= IPT_INV_ARPOP;
107 		break;
108 	case offsetof(struct arphdr, ar_hln):
109 		get_cmp_data(e, &ar_hln, sizeof(ar_hln), &inv);
110 		fw->arp.arhln = ar_hln;
111 		fw->arp.arhln_mask = 0xff;
112 		if (inv)
113 			fw->arp.invflags |= IPT_INV_ARPHLN;
114 		break;
115 	case offsetof(struct arphdr, ar_pln):
116 		get_cmp_data(e, &ar_pln, sizeof(ar_pln), &inv);
117 		if (ar_pln != 4 || inv)
118 			ctx->errmsg = "unexpected ARP protocol length match";
119 		break;
120 	default:
121 		if (reg->payload.offset == sizeof(struct arphdr)) {
122 			if (nft_arp_parse_devaddr(reg, e, &fw->arp.src_devaddr))
123 				fw->arp.invflags |= IPT_INV_SRCDEVADDR;
124 		} else if (reg->payload.offset == sizeof(struct arphdr) +
125 					   fw->arp.arhln) {
126 			get_cmp_data(e, &addr, sizeof(addr), &inv);
127 			fw->arp.src.s_addr = addr.s_addr;
128 			if (reg->bitwise.set)
129 				parse_mask_ipv4(reg, &fw->arp.smsk);
130 			else
131 				memset(&fw->arp.smsk, 0xff,
132 				       min(reg->payload.len,
133 					   sizeof(struct in_addr)));
134 
135 			if (inv)
136 				fw->arp.invflags |= IPT_INV_SRCIP;
137 		} else if (reg->payload.offset == sizeof(struct arphdr) +
138 						  fw->arp.arhln +
139 						  sizeof(struct in_addr)) {
140 			if (nft_arp_parse_devaddr(reg, e, &fw->arp.tgt_devaddr))
141 				fw->arp.invflags |= IPT_INV_TGTDEVADDR;
142 		} else if (reg->payload.offset == sizeof(struct arphdr) +
143 						  fw->arp.arhln +
144 						  sizeof(struct in_addr) +
145 						  fw->arp.arhln) {
146 			get_cmp_data(e, &addr, sizeof(addr), &inv);
147 			fw->arp.tgt.s_addr = addr.s_addr;
148 			if (reg->bitwise.set)
149 				parse_mask_ipv4(reg, &fw->arp.tmsk);
150 			else
151 				memset(&fw->arp.tmsk, 0xff,
152 				       min(reg->payload.len,
153 					   sizeof(struct in_addr)));
154 
155 			if (inv)
156 				fw->arp.invflags |= IPT_INV_DSTIP;
157 		} else {
158 			ctx->errmsg = "unknown payload offset";
159 		}
160 		break;
161 	}
162 }
163 
164 struct nft_ruleparse_ops nft_ruleparse_ops_arp = {
165 	.meta		= nft_arp_parse_meta,
166 	.payload	= nft_arp_parse_payload,
167 };
168