2 * (C) 2013 by Pablo Neira Ayuso <pablo@netfilter.org>
3 * (C) 2013 by Giuseppe Longo <giuseppelng@gmail.com>
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.
10 * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
19 #include <net/if_arp.h>
20 #include <netinet/if_ether.h>
22 #include <libnftnl/rule.h>
23 #include <libnftnl/expr.h>
25 #include "nft-shared.h"
26 #include "nft-ruleparse.h"
29 static void nft_arp_parse_meta(struct nft_xt_ctx
*ctx
,
30 const struct nft_xt_ctx_reg
*reg
,
32 struct iptables_command_state
*cs
)
34 struct arpt_entry
*fw
= &cs
->arp
;
37 if (parse_meta(ctx
, e
, reg
->meta_dreg
.key
, fw
->arp
.iniface
,
38 fw
->arp
.outiface
, &flags
) == 0) {
39 fw
->arp
.invflags
|= flags
;
43 ctx
->errmsg
= "Unknown arp meta key";
46 static void parse_mask_ipv4(const struct nft_xt_ctx_reg
*reg
, struct in_addr
*mask
)
48 mask
->s_addr
= reg
->bitwise
.mask
[0];
51 static bool nft_arp_parse_devaddr(const struct nft_xt_ctx_reg
*reg
,
53 struct arpt_devaddr_info
*info
)
58 nftnl_expr_get(e
, NFTNL_EXPR_CMP_DATA
, &hlen
);
63 get_cmp_data(e
, info
->addr
, ETH_ALEN
, &inv
);
66 memcpy(info
->mask
, reg
->bitwise
.mask
, ETH_ALEN
);
68 memset(info
->mask
, 0xff,
69 min(reg
->payload
.len
, ETH_ALEN
));
74 static void nft_arp_parse_payload(struct nft_xt_ctx
*ctx
,
75 const struct nft_xt_ctx_reg
*reg
,
77 struct iptables_command_state
*cs
)
79 struct arpt_entry
*fw
= &cs
->arp
;
81 uint16_t ar_hrd
, ar_pro
, ar_op
;
82 uint8_t ar_hln
, ar_pln
;
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;
91 fw
->arp
.invflags
|= IPT_INV_ARPHRD
;
93 fw
->arp
.arhrd_mask
= reg
->bitwise
.mask
[0];
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;
100 fw
->arp
.invflags
|= IPT_INV_PROTO
;
101 if (reg
->bitwise
.set
)
102 fw
->arp
.arpro_mask
= reg
->bitwise
.mask
[0];
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;
109 fw
->arp
.invflags
|= IPT_INV_ARPOP
;
110 if (reg
->bitwise
.set
)
111 fw
->arp
.arpop_mask
= reg
->bitwise
.mask
[0];
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;
118 fw
->arp
.invflags
|= IPT_INV_ARPHLN
;
119 if (reg
->bitwise
.set
)
120 fw
->arp
.arhln_mask
= reg
->bitwise
.mask
[0];
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";
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
) +
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
);
138 memset(&fw
->arp
.smsk
, 0xff,
139 min(reg
->payload
.len
,
140 sizeof(struct in_addr
)));
143 fw
->arp
.invflags
|= IPT_INV_SRCIP
;
144 } else if (reg
->payload
.offset
== sizeof(struct arphdr
) +
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
) +
151 sizeof(struct in_addr
) +
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
);
158 memset(&fw
->arp
.tmsk
, 0xff,
159 min(reg
->payload
.len
,
160 sizeof(struct in_addr
)));
163 fw
->arp
.invflags
|= IPT_INV_DSTIP
;
165 ctx
->errmsg
= "unknown payload offset";
171 struct nft_ruleparse_ops nft_ruleparse_ops_arp
= {
172 .meta
= nft_arp_parse_meta
,
173 .payload
= nft_arp_parse_payload
,