summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ip6t_MAP66.c68
1 files changed, 58 insertions, 10 deletions
diff --git a/ip6t_MAP66.c b/ip6t_MAP66.c
index 66f5172..2f880e5 100644
--- a/ip6t_MAP66.c
+++ b/ip6t_MAP66.c
@@ -7,6 +7,7 @@
#include <linux/module.h>
#include <linux/version.h>
#include <linux/netfilter_ipv6/ip6_tables.h>
+#include <linux/dccp.h>
#include <net/ipv6.h>
#include "ip6t_MAP66.h"
@@ -36,16 +37,31 @@ static inline u_int16_t csum16(const u_int16_t *buf, int len)
static void map16(
struct in6_addr* addr,
const struct in6_addr* to,
- int len,
- u_int16_t csum)
+ int len_to,
+ u_int16_t csum_to)
{
- csum = add16(
- *((u_int16_t*)addr + len),
- add16(csum16((const u_int16_t *)addr, len), csum)
+ csum_to = add16(
+ *((u_int16_t*)addr + len_to),
+ add16(csum16((const u_int16_t *)addr, len_to), csum_to)
);
- if (csum == 0xffff) csum = 0x0000;
- *((u_int16_t *)addr + len) = csum;
- memcpy(addr, to, sizeof(u_int16_t) * len);
+ if (csum_to == 0xffff) csum_to = 0x0000;
+ *((u_int16_t *)addr + len_to) = csum_to;
+ memcpy(addr, to, sizeof(u_int16_t) * len_to);
+}
+
+/* Perform mapping with csum update */
+static void map_csum(
+ struct in6_addr* addr,
+ const struct in6_addr* to,
+ int len_to,
+ u_int16_t csum_to,
+ u_int16_t* csum_transport)
+{
+ *csum_transport = ~add16(
+ ~csum_to,
+ add16(~*csum_transport, ~csum16((u_int16_t *)&addr, len_to))
+ );
+ memcpy(addr, to, sizeof(u_int16_t) * len_to);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
@@ -123,6 +139,7 @@ static unsigned int MAP66_tg6(
struct sk_buff *skb,
const struct xt_target_param *par)
{
+ u_int16_t* csum_transport = NULL;
struct ipv6hdr *hdr = ipv6_hdr(skb);
const struct ip6t_MAP66_info *info = par->targinfo;
@@ -151,13 +168,44 @@ static unsigned int MAP66_tg6(
hdr = ipv6_hdr(skb);
#endif
+ if (0 != (IP6T_MAP66_OPT_CSUM & info->mapflags)) {
+ u8 nexthdr = hdr->nexthdr;
+ int hoff = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr);
+ if (hoff < 0) {
+ pr_devel("MAP66: Unsupported packet dropped\n");
+ return NF_DROP;
+ }
+ switch(nexthdr) {
+ case IPPROTO_TCP:
+ csum_transport = skb_header_pointer(
+ skb, hoff+offsetof(struct tcphdr, check), 0, NULL);
+ break;
+ case IPPROTO_UDP:
+ case IPPROTO_UDPLITE:
+ csum_transport = skb_header_pointer(
+ skb, hoff+offsetof(struct udphdr, check), 0, NULL);
+ break;
+ case IPPROTO_DCCP:
+ csum_transport = skb_header_pointer(
+ skb, hoff+offsetof(struct dccp_hdr, dccph_checksum), 0, NULL);
+ break;
+ case IPPROTO_ICMPV6:
+ csum_transport = skb_header_pointer(
+ skb, hoff+offsetof(struct icmp6hdr, icmp6_cksum), 0, NULL);
+ break;
+ default:
+ pr_devel("MAP66: Unsupported protocol %d\n", nexthdr);
+ return NF_DROP;
+ }
+ }
+
if (0 != (IP6T_MAP66_OPT_DST_TO & info->mapflags)) {
pr_devel("MAP66: DST_TO, ip_summed=%d\n", skb->ip_summed);
if (0 != (IP6T_MAP66_OPT_NOCHECK & info->mapflags) || !is_my_ipv6_addr(
NF_INET_PRE_ROUTING == par->hooknum ? par->in : par->out, &hdr->daddr))
{
if (0 != (IP6T_MAP66_OPT_CSUM & info->mapflags)) {
- memcpy(&hdr->daddr, &info->pfix_dst_to, sizeof(u_int16_t) * info->pfix_dst_len);
+ map_csum(&hdr->daddr, &info->pfix_dst_to, info->pfix_dst_len, info->pfix_dst_csum, csum_transport);
}
else {
map16(&hdr->daddr, &info->pfix_dst_to, info->pfix_dst_len, info->pfix_dst_csum);
@@ -168,7 +216,7 @@ static unsigned int MAP66_tg6(
if (0 != (IP6T_MAP66_OPT_SRC_TO & info->mapflags)) {
pr_devel("MAP66: SRC_TO, ip_summed=%d\n", skb->ip_summed);
if (0 != (IP6T_MAP66_OPT_CSUM & info->mapflags)) {
- memcpy(&hdr->saddr, &info->pfix_src_to, sizeof(u_int16_t) * info->pfix_src_len);
+ map_csum(&hdr->saddr, &info->pfix_src_to, info->pfix_src_len, info->pfix_src_csum, csum_transport);
}
else {
map16(&hdr->saddr, &info->pfix_src_to, info->pfix_src_len, info->pfix_src_csum);