DHCP address match and mangler

This commit is contained in:
Jan Engelhardt
2008-09-01 14:26:21 -04:00
parent ab27472eb4
commit f30793f591
12 changed files with 470 additions and 0 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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");

View File

@@ -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
View 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);
}

View 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
View 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);
}

View 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
View 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
View 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
View 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 */

View File

@@ -2,6 +2,7 @@
#
build_CHAOS=m
build_DELUDE=m
build_DHCPADDR=m
build_ECHO=
build_IPMARK=m
build_LOGMARK=m