Add structures for identifiers, locators, and an ila address which is composed of a locator and identifier and in6_addr can be cast to it. This includes a three bit type field and enums for the types defined in ILA I-D. In ILA lwt don't allow user to set a translation for a non-ILA address (type of identifier is zero meaning it is an IID). This also requires that the destination prefix is at least 65 bytes (64 bit locator and first byte of identifier). Signed-off-by: Tom Herbert <tom@herbertland.com> Signed-off-by: David S. Miller <davem@davemloft.net>
108 lines
2.4 KiB
C
108 lines
2.4 KiB
C
#include <linux/errno.h>
|
|
#include <linux/ip.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/skbuff.h>
|
|
#include <linux/socket.h>
|
|
#include <linux/types.h>
|
|
#include <net/checksum.h>
|
|
#include <net/ip.h>
|
|
#include <net/ip6_fib.h>
|
|
#include <net/lwtunnel.h>
|
|
#include <net/protocol.h>
|
|
#include <uapi/linux/ila.h>
|
|
#include "ila.h"
|
|
|
|
static __wsum get_csum_diff(struct ipv6hdr *ip6h, struct ila_params *p)
|
|
{
|
|
struct ila_addr *iaddr = ila_a2i(&ip6h->daddr);
|
|
|
|
if (iaddr->loc.v64 == p->locator_match.v64)
|
|
return p->csum_diff;
|
|
else
|
|
return compute_csum_diff8((__be32 *)&iaddr->loc,
|
|
(__be32 *)&p->locator);
|
|
}
|
|
|
|
void ila_update_ipv6_locator(struct sk_buff *skb, struct ila_params *p)
|
|
{
|
|
__wsum diff;
|
|
struct ipv6hdr *ip6h = ipv6_hdr(skb);
|
|
struct ila_addr *iaddr = ila_a2i(&ip6h->daddr);
|
|
size_t nhoff = sizeof(struct ipv6hdr);
|
|
|
|
/* First update checksum */
|
|
switch (ip6h->nexthdr) {
|
|
case NEXTHDR_TCP:
|
|
if (likely(pskb_may_pull(skb, nhoff + sizeof(struct tcphdr)))) {
|
|
struct tcphdr *th = (struct tcphdr *)
|
|
(skb_network_header(skb) + nhoff);
|
|
|
|
diff = get_csum_diff(ip6h, p);
|
|
inet_proto_csum_replace_by_diff(&th->check, skb,
|
|
diff, true);
|
|
}
|
|
break;
|
|
case NEXTHDR_UDP:
|
|
if (likely(pskb_may_pull(skb, nhoff + sizeof(struct udphdr)))) {
|
|
struct udphdr *uh = (struct udphdr *)
|
|
(skb_network_header(skb) + nhoff);
|
|
|
|
if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) {
|
|
diff = get_csum_diff(ip6h, p);
|
|
inet_proto_csum_replace_by_diff(&uh->check, skb,
|
|
diff, true);
|
|
if (!uh->check)
|
|
uh->check = CSUM_MANGLED_0;
|
|
}
|
|
}
|
|
break;
|
|
case NEXTHDR_ICMP:
|
|
if (likely(pskb_may_pull(skb,
|
|
nhoff + sizeof(struct icmp6hdr)))) {
|
|
struct icmp6hdr *ih = (struct icmp6hdr *)
|
|
(skb_network_header(skb) + nhoff);
|
|
|
|
diff = get_csum_diff(ip6h, p);
|
|
inet_proto_csum_replace_by_diff(&ih->icmp6_cksum, skb,
|
|
diff, true);
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* Now change destination address */
|
|
iaddr->loc = p->locator;
|
|
}
|
|
|
|
static int __init ila_init(void)
|
|
{
|
|
int ret;
|
|
|
|
ret = ila_lwt_init();
|
|
|
|
if (ret)
|
|
goto fail_lwt;
|
|
|
|
ret = ila_xlat_init();
|
|
if (ret)
|
|
goto fail_xlat;
|
|
|
|
return 0;
|
|
fail_xlat:
|
|
ila_lwt_fini();
|
|
fail_lwt:
|
|
return ret;
|
|
}
|
|
|
|
static void __exit ila_fini(void)
|
|
{
|
|
ila_xlat_fini();
|
|
ila_lwt_fini();
|
|
}
|
|
|
|
module_init(ila_init);
|
|
module_exit(ila_fini);
|
|
MODULE_ALIAS_RTNL_LWT(ILA);
|
|
MODULE_AUTHOR("Tom Herbert <tom@herbertland.com>");
|
|
MODULE_LICENSE("GPL");
|