mirror of
git://git.code.sf.net/p/xtables-addons/xtables-addons
synced 2025-09-05 20:26:38 +02:00
DHCP address match and mangler
This commit is contained in:
@@ -7,6 +7,7 @@ obj-m += compat_xtables.o
|
||||
|
||||
obj-${build_CHAOS} += xt_CHAOS.o
|
||||
obj-${build_DELUDE} += xt_DELUDE.o
|
||||
obj-${build_DHCPADDR} += xt_DHCPADDR.o
|
||||
obj-${build_ECHO} += xt_ECHO.o
|
||||
obj-${build_IPMARK} += xt_IPMARK.o
|
||||
obj-${build_LOGMARK} += xt_LOGMARK.o
|
||||
|
@@ -1,5 +1,6 @@
|
||||
obj-${build_CHAOS} += libxt_CHAOS.so
|
||||
obj-${build_DELUDE} += libxt_DELUDE.so
|
||||
obj-${build_DHCPADDR} += libxt_DHCPADDR.so libxt_dhcpaddr.so
|
||||
obj-${build_ECHO} += libxt_ECHO.so
|
||||
obj-${build_IPMARK} += libxt_IPMARK.so
|
||||
obj-${build_LOGMARK} += libxt_LOGMARK.so
|
||||
|
@@ -383,6 +383,19 @@ int xtnu_neigh_hh_output(struct hh_cache *hh, struct sk_buff *skb)
|
||||
return hh->hh_output(skb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xtnu_neigh_hh_output);
|
||||
|
||||
static inline void csum_replace4(__sum16 *sum, __be32 from, __be32 to)
|
||||
{
|
||||
__be32 diff[] = {~from, to};
|
||||
*sum = csum_fold(csum_partial((char *)diff, sizeof(diff),
|
||||
~csum_unfold(*sum)));
|
||||
}
|
||||
|
||||
void xtnu_csum_replace2(__sum16 *sum, __be16 from, __be16 to)
|
||||
{
|
||||
csum_replace4(sum, (__force __be32)from, (__force __be32)to);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xtnu_csum_replace2);
|
||||
#endif
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@@ -59,6 +59,12 @@
|
||||
# define xt_unregister_matches xtnu_unregister_matches
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 19)
|
||||
# define csum_replace2 xtnu_csum_replace2
|
||||
#elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24)
|
||||
# define csum_replace2 nf_csum_replace2
|
||||
#endif
|
||||
|
||||
#define ip_route_me_harder xtnu_ip_route_me_harder
|
||||
#define skb_make_writable xtnu_skb_make_writable
|
||||
#define xt_target xtnu_target
|
||||
|
101
extensions/libxt_DHCPADDR.c
Normal file
101
extensions/libxt_DHCPADDR.c
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* "DHCPADDR" target extension for iptables
|
||||
* Copyright © Jan Engelhardt <jengelh [at] medozas de>, 2008
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License; either
|
||||
* version 2 of the License, or any later version, as published by the
|
||||
* Free Software Foundation.
|
||||
*/
|
||||
#include <getopt.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <netinet/ether.h>
|
||||
#include <xtables.h>
|
||||
#include "xt_DHCPADDR.h"
|
||||
#include "mac.c"
|
||||
|
||||
enum {
|
||||
F_MAC = 1 << 0,
|
||||
};
|
||||
|
||||
static const struct option dhcpaddr_tg_opts[] = {
|
||||
{.name = "set-mac", .has_arg = true, .val = 'M'},
|
||||
{NULL},
|
||||
};
|
||||
|
||||
static void dhcpaddr_tg_help(void)
|
||||
{
|
||||
printf(
|
||||
"DHCPADDDR target options:\n"
|
||||
" --set-mac lladdr[/mask] Set MAC address in DHCP Client Host field\n"
|
||||
);
|
||||
}
|
||||
|
||||
static int dhcpaddr_tg_parse(int c, char **argv, int invert,
|
||||
unsigned int *flags, const void *entry, struct xt_entry_target **target)
|
||||
{
|
||||
struct dhcpaddr_info *info = (void *)(*target)->data;
|
||||
|
||||
switch (c) {
|
||||
case 'M':
|
||||
param_act(P_ONLY_ONCE, "DHCPADDR", "--set-mac", *flags & F_MAC);
|
||||
param_act(P_NO_INVERT, "DHCPADDR", "--set-mac", invert);
|
||||
if (!mac_parse(optarg, info->addr, &info->mask))
|
||||
param_act(P_BAD_VALUE, "DHCPADDR", "--set-mac", optarg);
|
||||
*flags |= F_MAC;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void dhcpaddr_tg_check(unsigned int flags)
|
||||
{
|
||||
if (flags == 0)
|
||||
exit_error(PARAMETER_PROBLEM, "DHCPADDR target: "
|
||||
"--set-mac parameter required");
|
||||
}
|
||||
|
||||
static void dhcpaddr_tg_print(const void *ip,
|
||||
const struct xt_entry_target *target, int numeric)
|
||||
{
|
||||
const struct dhcpaddr_info *info = (void *)target->data;
|
||||
|
||||
printf("DHCPADDR %s" DH_MAC_FMT "/%u ",
|
||||
info->invert ? "!" : "", DH_MAC_HEX(info->addr), info->mask);
|
||||
}
|
||||
|
||||
static void dhcpaddr_tg_save(const void *ip,
|
||||
const struct xt_entry_target *target)
|
||||
{
|
||||
const struct dhcpaddr_info *info = (const void *)target->data;
|
||||
|
||||
if (info->invert)
|
||||
printf("! ");
|
||||
printf("--set-mac " DH_MAC_FMT "/%u ",
|
||||
DH_MAC_HEX(info->addr), info->mask);
|
||||
}
|
||||
|
||||
static struct xtables_target dhcpaddr_tg_reg = {
|
||||
.version = XTABLES_VERSION,
|
||||
.name = "DHCPADDR",
|
||||
.revision = 0,
|
||||
.family = PF_INET,
|
||||
.size = XT_ALIGN(sizeof(struct dhcpaddr_info)),
|
||||
.userspacesize = XT_ALIGN(sizeof(struct dhcpaddr_info)),
|
||||
.help = dhcpaddr_tg_help,
|
||||
.parse = dhcpaddr_tg_parse,
|
||||
.final_check = dhcpaddr_tg_check,
|
||||
.print = dhcpaddr_tg_print,
|
||||
.save = dhcpaddr_tg_save,
|
||||
.extra_opts = dhcpaddr_tg_opts,
|
||||
};
|
||||
|
||||
static void _init(void)
|
||||
{
|
||||
xtables_register_target(&dhcpaddr_tg_reg);
|
||||
}
|
25
extensions/libxt_DHCPADDR.man
Normal file
25
extensions/libxt_DHCPADDR.man
Normal file
@@ -0,0 +1,25 @@
|
||||
In conjunction with ebtables, DHCPADDR can be used to completely change all MAC
|
||||
addresses from and to a VMware-based virtual machine. This is needed because
|
||||
VMware does not allow to set a non-VMware MAC address before an operating
|
||||
system is booted (and the MAC be changed with `ip link set eth0 address
|
||||
aa:bb..`).
|
||||
.TP
|
||||
\fB--set-mac\fP \fIaa:bb:cc:dd:ee:ff\fP[\fB/\fP\fImask\fP]
|
||||
Replace the client host MAC address field in the DHCP message with the given
|
||||
MAC address. This option is mandatory. The \fImask\fP parameter specifies the
|
||||
prefix length of bits to change.
|
||||
.PP
|
||||
EXAMPLE, replacing all addresses from one of VMware's assigned vendor IDs
|
||||
(00:50:56) addresses with something else:
|
||||
.PP
|
||||
iptables -t mangle -A FORWARD -p udp --dport 67 -m physdev --physdev-in vmnet1
|
||||
-m dhcpaddr --mac 00:50:56:00:00:00/24 -j DHCPADDR --set-mac
|
||||
ab:cd:ef:00:00:00/24
|
||||
.PP
|
||||
iptables -t mangle -A FORWARD -p udp --dport 68 -m physdev --physdev-out vmnet1
|
||||
-m dhcpaddr --mac ab:cd:ef:00:00:00/24 -j DHCPADDR --set-mac
|
||||
00:50:56:00:00:00/24
|
||||
.PP
|
||||
(This assumes there is a bridge interface that has vmnet1 as a port. You will
|
||||
also need to add appropriate ebtables rules to change the MAC address of the
|
||||
Ethernet headers.)
|
102
extensions/libxt_dhcpaddr.c
Normal file
102
extensions/libxt_dhcpaddr.c
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* "dhcpaddr" match extension for iptables
|
||||
* Copyright © Jan Engelhardt <jengelh [at] medozas de>, 2008
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License; either
|
||||
* version 2 of the License, or any later version, as published by the
|
||||
* Free Software Foundation.
|
||||
*/
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <netdb.h>
|
||||
#include <net/ethernet.h>
|
||||
#include <xtables.h>
|
||||
#include "xt_DHCPADDR.h"
|
||||
#include "mac.c"
|
||||
|
||||
enum {
|
||||
F_MAC = 1 << 0,
|
||||
};
|
||||
|
||||
static const struct option dhcpaddr_mt_opts[] = {
|
||||
{.name = "mac", .has_arg = true, .val = 'M'},
|
||||
{NULL},
|
||||
};
|
||||
|
||||
static void dhcpaddr_mt_help(void)
|
||||
{
|
||||
printf(
|
||||
"dhcpaddr match options:\n"
|
||||
"[!] --mac lladdr[/mask] Match on MAC address in DHCP Client Host field\n"
|
||||
);
|
||||
}
|
||||
|
||||
static int dhcpaddr_mt_parse(int c, char **argv, int invert,
|
||||
unsigned int *flags, const void *entry, struct xt_entry_match **match)
|
||||
{
|
||||
struct dhcpaddr_info *info = (void *)(*match)->data;
|
||||
|
||||
switch (c) {
|
||||
case 'M':
|
||||
param_act(P_ONLY_ONCE, "dhcpaddr", "--mac", *flags & F_MAC);
|
||||
param_act(P_NO_INVERT, "dhcpaddr", "--mac", invert);
|
||||
if (!mac_parse(optarg, info->addr, &info->mask))
|
||||
param_act(P_BAD_VALUE, "dhcpaddr", "--mac", optarg);
|
||||
if (invert)
|
||||
info->invert = true;
|
||||
*flags |= F_MAC;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void dhcpaddr_mt_check(unsigned int flags)
|
||||
{
|
||||
if (flags == 0)
|
||||
exit_error(PARAMETER_PROBLEM, "dhcpaddr match: "
|
||||
"--mac parameter required");
|
||||
}
|
||||
|
||||
static void dhcpaddr_mt_print(const void *ip,
|
||||
const struct xt_entry_match *match, int numeric)
|
||||
{
|
||||
const struct dhcpaddr_info *info = (void *)match->data;
|
||||
|
||||
printf("dhcpaddr %s" DH_MAC_FMT "/%u ",
|
||||
info->invert ? "!" : "", DH_MAC_HEX(info->addr), info->mask);
|
||||
}
|
||||
|
||||
static void dhcpaddr_mt_save(const void *ip,
|
||||
const struct xt_entry_match *match)
|
||||
{
|
||||
const struct dhcpaddr_info *info = (void *)match->data;
|
||||
|
||||
if (info->invert)
|
||||
printf("! ");
|
||||
printf("--mac " DH_MAC_FMT "/%u ",
|
||||
DH_MAC_HEX(info->addr), info->mask);
|
||||
}
|
||||
|
||||
static struct xtables_match dhcpaddr_mt_reg = {
|
||||
.version = XTABLES_VERSION,
|
||||
.name = "dhcpaddr",
|
||||
.revision = 0,
|
||||
.family = PF_INET,
|
||||
.size = XT_ALIGN(sizeof(struct dhcpaddr_info)),
|
||||
.userspacesize = XT_ALIGN(sizeof(struct dhcpaddr_info)),
|
||||
.help = dhcpaddr_mt_help,
|
||||
.parse = dhcpaddr_mt_parse,
|
||||
.final_check = dhcpaddr_mt_check,
|
||||
.print = dhcpaddr_mt_print,
|
||||
.save = dhcpaddr_mt_save,
|
||||
.extra_opts = dhcpaddr_mt_opts,
|
||||
};
|
||||
|
||||
static void _init(void)
|
||||
{
|
||||
xtables_register_match(&dhcpaddr_mt_reg);
|
||||
}
|
4
extensions/libxt_dhcpaddr.man
Normal file
4
extensions/libxt_dhcpaddr.man
Normal file
@@ -0,0 +1,4 @@
|
||||
.TP
|
||||
\fB--mac\fP \fIaa:bb:cc:dd:ee:ff\fP[\fB/\fP\fImask\fP]
|
||||
Matches the DHCP Client Host address in a DHCP message. \fImask\fP specifies
|
||||
the prefix length of the initial portion to match.
|
29
extensions/mac.c
Normal file
29
extensions/mac.c
Normal file
@@ -0,0 +1,29 @@
|
||||
static bool mac_parse(const char *addr, unsigned char *dest, uint8_t *mask)
|
||||
{
|
||||
unsigned int i = 0, value;
|
||||
char *end;
|
||||
|
||||
for (i = 0; i < ETH_ALEN; ++i) {
|
||||
value = strtoul(addr, &end, 16);
|
||||
if (addr == end || value > 0xFF)
|
||||
return false;
|
||||
if (i == ETH_ALEN - 1) {
|
||||
if (*end != '\0' && *end != '/')
|
||||
return false;
|
||||
} else if (*end != ':') {
|
||||
return false;
|
||||
}
|
||||
dest[i] = value;
|
||||
addr = end + 1;
|
||||
}
|
||||
|
||||
*mask = 48;
|
||||
if (*end == '/') {
|
||||
if (!strtonum(end + 1, &end, &value, 0, 48))
|
||||
return false;
|
||||
if (*end != '\0')
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
175
extensions/xt_DHCPADDR.c
Normal file
175
extensions/xt_DHCPADDR.c
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* "DHCPADDR" extensions for Xtables
|
||||
* Copyright © Jan Engelhardt <jengelh [at] medozas de>, 2008
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License; either
|
||||
* version 2 of the License, or any later version, as published by the
|
||||
* Free Software Foundation.
|
||||
*/
|
||||
#include <linux/ip.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/udp.h>
|
||||
#include <net/ip.h>
|
||||
#include <linux/netfilter/x_tables.h>
|
||||
#include "xt_DHCPADDR.h"
|
||||
#include "compat_xtables.h"
|
||||
|
||||
struct dhcp_message {
|
||||
uint8_t op, htype, hlen, hops;
|
||||
__be32 xid;
|
||||
__be16 secs, flags;
|
||||
__be32 ciaddr, yiaddr, siaddr, giaddr;
|
||||
char chaddr[16];
|
||||
/* Omitting all unneeded fields saves runtime memory */
|
||||
/* char sname[64], file[128]; */
|
||||
};
|
||||
|
||||
static void ether_set(unsigned char *addr, const unsigned char *op,
|
||||
uint8_t mask)
|
||||
{
|
||||
uint8_t lo_mask;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ETH_ALEN && mask > 0; ++i) {
|
||||
lo_mask = mask % 8;
|
||||
/* FF << 4 >> 4 = 0F */
|
||||
lo_mask = ~(uint8_t)0U << lo_mask >> lo_mask;
|
||||
addr[i] &= lo_mask;
|
||||
addr[i] |= op[i] & ~lo_mask;
|
||||
if (mask >= 8)
|
||||
mask -= 8;
|
||||
else
|
||||
mask = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool ether_cmp(const unsigned char *lh, const unsigned char *rh,
|
||||
uint8_t mask)
|
||||
{
|
||||
uint8_t lo_mask;
|
||||
unsigned int i;
|
||||
#define ZMAC_FMT "%02X:%02X:%02X:%02X:%02X:%02X"
|
||||
#define ZMACHEX(s) s[0], s[1], s[2], s[3], s[4], s[5]
|
||||
|
||||
for (i = 0; i < ETH_ALEN && mask > 0; ++i) {
|
||||
lo_mask = mask % 8;
|
||||
/* ~(0xFF << 4 >> 4) = ~0x0F = 0xF0 */
|
||||
lo_mask = ~(~(uint8_t)0U << lo_mask >> lo_mask);
|
||||
if ((lh[i] ^ rh[i]) & lo_mask)
|
||||
return false;
|
||||
if (mask >= 8)
|
||||
mask -= 8;
|
||||
else
|
||||
mask = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool dhcpaddr_mt(const struct sk_buff *skb, const struct net_device *in,
|
||||
const struct net_device *out, const struct xt_match *match,
|
||||
const void *matchinfo, int offset, unsigned int protoff, bool *hotdrop)
|
||||
{
|
||||
const struct dhcpaddr_info *info = matchinfo;
|
||||
const struct dhcp_message *dh;
|
||||
struct dhcp_message dhcpbuf;
|
||||
|
||||
dh = skb_header_pointer(skb, protoff + sizeof(struct udphdr),
|
||||
sizeof(dhcpbuf), &dhcpbuf);
|
||||
if (dh == NULL)
|
||||
/*
|
||||
* No hotdrop. This packet does not look like DHCP, but other
|
||||
* matches may still have a valid reason to get their chance
|
||||
* to match on this.
|
||||
*/
|
||||
return false;
|
||||
|
||||
return ether_cmp((const void *)dh->chaddr, info->addr, info->mask);
|
||||
}
|
||||
|
||||
static unsigned int dhcpaddr_tg(struct sk_buff **pskb,
|
||||
const struct net_device *in, const struct net_device *out,
|
||||
unsigned int hooknum, const struct xt_target *target, const void *targinfo)
|
||||
{
|
||||
const struct dhcpaddr_info *info = targinfo;
|
||||
struct dhcp_message dhcpbuf, *dh;
|
||||
struct udphdr udpbuf, *udph;
|
||||
struct sk_buff *skb = *pskb;
|
||||
unsigned int i;
|
||||
|
||||
if (!skb_make_writable(pskb, 0))
|
||||
return NF_DROP;
|
||||
|
||||
udph = skb_header_pointer(skb, ip_hdrlen(skb),
|
||||
sizeof(udpbuf), &udpbuf);
|
||||
if (udph == NULL)
|
||||
return NF_DROP;
|
||||
|
||||
dh = skb_header_pointer(skb, ip_hdrlen(skb) + sizeof(udpbuf),
|
||||
sizeof(dhcpbuf), &dhcpbuf);
|
||||
if (dh == NULL)
|
||||
return NF_DROP;
|
||||
|
||||
for (i = 0; i < sizeof(dh->chaddr); i += 2)
|
||||
csum_replace2(&udph->check, *(const __be16 *)dh->chaddr, 0);
|
||||
|
||||
memset(dh->chaddr, 0, sizeof(dh->chaddr));
|
||||
ether_set(dh->chaddr, info->addr, info->mask);
|
||||
|
||||
for (i = 0; i < sizeof(dh->chaddr); i += 2)
|
||||
csum_replace2(&udph->check, 0, *(const __be16 *)dh->chaddr);
|
||||
|
||||
return XT_CONTINUE;
|
||||
}
|
||||
|
||||
static struct xt_target dhcpaddr_tg_reg __read_mostly = {
|
||||
.name = "DHCPADDR",
|
||||
.revision = 0,
|
||||
.family = PF_INET,
|
||||
.proto = IPPROTO_UDP,
|
||||
.table = "mangle",
|
||||
.target = dhcpaddr_tg,
|
||||
.targetsize = XT_ALIGN(sizeof(struct dhcpaddr_info)),
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct xt_match dhcpaddr_mt_reg __read_mostly = {
|
||||
.name = "dhcpaddr",
|
||||
.revision = 0,
|
||||
.family = PF_INET,
|
||||
.proto = IPPROTO_UDP,
|
||||
.match = dhcpaddr_mt,
|
||||
.matchsize = XT_ALIGN(sizeof(struct dhcpaddr_info)),
|
||||
.me = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init dhcpaddr_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = xt_register_target(&dhcpaddr_tg_reg);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
ret = xt_register_match(&dhcpaddr_mt_reg);
|
||||
if (ret != 0) {
|
||||
xt_unregister_target(&dhcpaddr_tg_reg);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit dhcpaddr_exit(void)
|
||||
{
|
||||
xt_unregister_target(&dhcpaddr_tg_reg);
|
||||
xt_unregister_match(&dhcpaddr_mt_reg);
|
||||
}
|
||||
|
||||
module_init(dhcpaddr_init);
|
||||
module_exit(dhcpaddr_exit);
|
||||
MODULE_DESCRIPTION("Xtables: Clamp DHCP MAC to packet MAC addresses");
|
||||
MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("ipt_DHCPADDR");
|
||||
MODULE_ALIAS("ipt_dhcpaddr");
|
12
extensions/xt_DHCPADDR.h
Normal file
12
extensions/xt_DHCPADDR.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef _LINUX_NETFILTER_XT_DHCPADDR_H
|
||||
#define _LINUX_NETFILTER_XT_DHCPADDR_H 1
|
||||
|
||||
#define DH_MAC_FMT "%02X:%02X:%02X:%02X:%02X:%02X"
|
||||
#define DH_MAC_HEX(z) z[0], z[1], z[2], z[3], z[4], z[5]
|
||||
|
||||
struct dhcpaddr_info {
|
||||
unsigned char addr[ETH_ALEN];
|
||||
uint8_t mask, invert;
|
||||
};
|
||||
|
||||
#endif /* _LINUX_NETFILTER_XT_DHCPADDR_H */
|
Reference in New Issue
Block a user