/* * ebt_idnat * * Authors: * Grzegorz Borowiak * * September, 2003 * */ #include #include #include #include #include #include #include static int get_ip_dst(const struct sk_buff *skb, uint32_t *addr) { if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_IP)) { *addr = skb->nh.iph->daddr; return 1; } if (skb->mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) { uint32_t arp_len = sizeof(struct arphdr) + (2 * (((*skb).nh.arph)->ar_hln)) + (2 * (((*skb).nh.arph)->ar_pln)); // Make sure the packet is long enough. if ((((*skb).nh.raw) + arp_len) > (*skb).tail) return 0; // IPv4 addresses are always 4 bytes. if (((*skb).nh.arph)->ar_pln != sizeof(uint32_t)) return 0; memcpy(addr, ((*skb).nh.raw) + sizeof(struct arphdr) + (2*(((*skb).nh.arph)->ar_hln)) + (((*skb).nh.arph)->ar_pln), sizeof(uint32_t)); return 2; } return 0; } static int ebt_target_idnat(struct sk_buff **pskb, unsigned int hooknr, const struct net_device *in, const struct net_device *out, const void *data, unsigned int datalen) { struct ebt_inat_info *info = (struct ebt_inat_info *)data; uint32_t ip; int index; struct ebt_inat_tuple *tuple; if (!get_ip_dst(*pskb, &ip)) { /* not an ARP or IPV4 packet */ return info->target; } if ((ip & __constant_htonl(0xffffff00)) != info->ip_subnet) { /* outside our range */ return info->target; } index = ((unsigned char*)&ip)[3]; /* the last byte; network packets are big endian */ tuple = &info->a[index]; if (!tuple->enabled) { /* we do not want to alter packets with this IP */ return info->target; } memcpy(((**pskb).mac.ethernet)->h_dest, tuple->mac, ETH_ALEN * sizeof(unsigned char)); if ((**pskb).mac.ethernet->h_proto == __constant_htons(ETH_P_ARP)) { /* change the payload */ memcpy( (**pskb).nh.raw + sizeof(struct arphdr) + (**pskb).nh.arph->ar_hln + (**pskb).nh.arph->ar_pln, tuple->mac, ETH_ALEN); } return tuple->target; } static int ebt_target_idnat_check(const char *tablename, unsigned int hookmask, const struct ebt_entry *e, void *data, unsigned int datalen) { struct ebt_inat_info *info = (struct ebt_inat_info *)data; if (BASE_CHAIN && info->target == EBT_RETURN) return -EINVAL; CLEAR_BASE_CHAIN_BIT; if ( (strcmp(tablename, "nat") || (hookmask & ~((1 << NF_BR_PRE_ROUTING) | (1 << NF_BR_LOCAL_OUT)))) && (strcmp(tablename, "broute") || hookmask & ~(1 << NF_BR_BROUTING)) ) return -EINVAL; if (datalen != EBT_ALIGN(sizeof(struct ebt_inat_info))) return -EINVAL; if (INVALID_TARGET) return -EINVAL; return 0; } static struct ebt_target idnat = { {NULL, NULL}, EBT_IDNAT_TARGET, ebt_target_idnat, ebt_target_idnat_check, NULL, THIS_MODULE }; static int __init init(void) { return ebt_register_target(&idnat); } static void __exit fini(void) { ebt_unregister_target(&idnat); } module_init(init); module_exit(fini); EXPORT_NO_SYMBOLS; MODULE_LICENSE("GPL");