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