• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
3  * (C) 2013 by Giuseppe Longo <giuseppelng@gmail.com>
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,
38 		       fw->arp.outiface, &flags) == 0) {
39 		fw->arp.invflags |= flags;
40 		return;
41 	}
42 
43 	ctx->errmsg = "Unknown arp meta key";
44 }
45 
parse_mask_ipv4(const struct nft_xt_ctx_reg * reg,struct in_addr * mask)46 static void parse_mask_ipv4(const struct nft_xt_ctx_reg *reg, struct in_addr *mask)
47 {
48 	mask->s_addr = reg->bitwise.mask[0];
49 }
50 
nft_arp_parse_devaddr(const struct nft_xt_ctx_reg * reg,struct nftnl_expr * e,struct arpt_devaddr_info * info)51 static bool nft_arp_parse_devaddr(const struct nft_xt_ctx_reg *reg,
52 				  struct nftnl_expr *e,
53 				  struct arpt_devaddr_info *info)
54 {
55 	uint32_t hlen;
56 	bool inv;
57 
58 	nftnl_expr_get(e, NFTNL_EXPR_CMP_DATA, &hlen);
59 
60 	if (hlen != ETH_ALEN)
61 		return false;
62 
63 	get_cmp_data(e, info->addr, ETH_ALEN, &inv);
64 
65 	if (reg->bitwise.set)
66 		memcpy(info->mask, reg->bitwise.mask, ETH_ALEN);
67 	else
68 		memset(info->mask, 0xff,
69 		       min(reg->payload.len, ETH_ALEN));
70 
71 	return inv;
72 }
73 
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)74 static void nft_arp_parse_payload(struct nft_xt_ctx *ctx,
75 				  const struct nft_xt_ctx_reg *reg,
76 				  struct nftnl_expr *e,
77 				  struct iptables_command_state *cs)
78 {
79 	struct arpt_entry *fw = &cs->arp;
80 	struct in_addr addr;
81 	uint16_t ar_hrd, ar_pro, ar_op;
82 	uint8_t ar_hln, ar_pln;
83 	bool inv;
84 
85 	switch (reg->payload.offset) {
86 	case offsetof(struct arphdr, ar_hrd):
87 		get_cmp_data(e, &ar_hrd, sizeof(ar_hrd), &inv);
88 		fw->arp.arhrd = ar_hrd;
89 		fw->arp.arhrd_mask = 0xffff;
90 		if (inv)
91 			fw->arp.invflags |= IPT_INV_ARPHRD;
92 		if (reg->bitwise.set)
93 			fw->arp.arhrd_mask = reg->bitwise.mask[0];
94 		break;
95 	case offsetof(struct arphdr, ar_pro):
96 		get_cmp_data(e, &ar_pro, sizeof(ar_pro), &inv);
97 		fw->arp.arpro = ar_pro;
98 		fw->arp.arpro_mask = 0xffff;
99 		if (inv)
100 			fw->arp.invflags |= IPT_INV_PROTO;
101 		if (reg->bitwise.set)
102 			fw->arp.arpro_mask = reg->bitwise.mask[0];
103 		break;
104 	case offsetof(struct arphdr, ar_op):
105 		get_cmp_data(e, &ar_op, sizeof(ar_op), &inv);
106 		fw->arp.arpop = ar_op;
107 		fw->arp.arpop_mask = 0xffff;
108 		if (inv)
109 			fw->arp.invflags |= IPT_INV_ARPOP;
110 		if (reg->bitwise.set)
111 			fw->arp.arpop_mask = reg->bitwise.mask[0];
112 		break;
113 	case offsetof(struct arphdr, ar_hln):
114 		get_cmp_data(e, &ar_hln, sizeof(ar_hln), &inv);
115 		fw->arp.arhln = ar_hln;
116 		fw->arp.arhln_mask = 0xff;
117 		if (inv)
118 			fw->arp.invflags |= IPT_INV_ARPHLN;
119 		if (reg->bitwise.set)
120 			fw->arp.arhln_mask = reg->bitwise.mask[0];
121 		break;
122 	case offsetof(struct arphdr, ar_pln):
123 		get_cmp_data(e, &ar_pln, sizeof(ar_pln), &inv);
124 		if (ar_pln != 4 || inv)
125 			ctx->errmsg = "unexpected ARP protocol length match";
126 		break;
127 	default:
128 		if (reg->payload.offset == sizeof(struct arphdr)) {
129 			if (nft_arp_parse_devaddr(reg, e, &fw->arp.src_devaddr))
130 				fw->arp.invflags |= IPT_INV_SRCDEVADDR;
131 		} else if (reg->payload.offset == sizeof(struct arphdr) +
132 					   fw->arp.arhln) {
133 			get_cmp_data(e, &addr, sizeof(addr), &inv);
134 			fw->arp.src.s_addr = addr.s_addr;
135 			if (reg->bitwise.set)
136 				parse_mask_ipv4(reg, &fw->arp.smsk);
137 			else
138 				memset(&fw->arp.smsk, 0xff,
139 				       min(reg->payload.len,
140 					   sizeof(struct in_addr)));
141 
142 			if (inv)
143 				fw->arp.invflags |= IPT_INV_SRCIP;
144 		} else if (reg->payload.offset == sizeof(struct arphdr) +
145 						  fw->arp.arhln +
146 						  sizeof(struct in_addr)) {
147 			if (nft_arp_parse_devaddr(reg, e, &fw->arp.tgt_devaddr))
148 				fw->arp.invflags |= IPT_INV_TGTDEVADDR;
149 		} else if (reg->payload.offset == sizeof(struct arphdr) +
150 						  fw->arp.arhln +
151 						  sizeof(struct in_addr) +
152 						  fw->arp.arhln) {
153 			get_cmp_data(e, &addr, sizeof(addr), &inv);
154 			fw->arp.tgt.s_addr = addr.s_addr;
155 			if (reg->bitwise.set)
156 				parse_mask_ipv4(reg, &fw->arp.tmsk);
157 			else
158 				memset(&fw->arp.tmsk, 0xff,
159 				       min(reg->payload.len,
160 					   sizeof(struct in_addr)));
161 
162 			if (inv)
163 				fw->arp.invflags |= IPT_INV_DSTIP;
164 		} else {
165 			ctx->errmsg = "unknown payload offset";
166 		}
167 		break;
168 	}
169 }
170 
171 struct nft_ruleparse_ops nft_ruleparse_ops_arp = {
172 	.meta		= nft_arp_parse_meta,
173 	.payload	= nft_arp_parse_payload,
174 };
175