From 8d94f2020d372385156e9618d6279ba964ba8870 Mon Sep 17 00:00:00 2001 From: fendo Date: Thu, 18 Sep 2014 18:16:21 +0200 Subject: [PATCH] new feature: If the user attribute "Framed-IPv6-Address" is defined then the ICMPv6_RA advertise this IPv6 address. Fix: Incorrect delegation of IPv6 prefixes when multiple of 4 bits (nibble) (eg: /44, /52 ...). --- icmp.c | 8 ++++++- l2tpns.c | 63 +++++++++++++++++++++++++++++++++++++------------------- l2tpns.h | 2 ++ ppp.c | 25 +++++++++++++++++++--- 4 files changed, 73 insertions(+), 25 deletions(-) diff --git a/icmp.c b/icmp.c index 0d6c65e..d2f53e0 100644 --- a/icmp.c +++ b/icmp.c @@ -143,7 +143,13 @@ void send_ipv6_ra(sessionidt s, tunnelidt t, struct in6_addr *ip) pinfo->nd_opt_pi_preferred_time = htonl(604800); pinfo->nd_opt_pi_reserved2 = 0; pinfo->nd_opt_pi_prefix_len = 64; // prefix length - pinfo->nd_opt_pi_prefix = config->ipv6_prefix; + if (session[s].ipv6address.s6_addr[0]) + { + // MSB 64bits of assigned IPv6 address to user (see radius attribut Framed-IPv6-Address) + memcpy(&pinfo->nd_opt_pi_prefix, &session[s].ipv6address.s6_addr[0], 8); + } + else + pinfo->nd_opt_pi_prefix = config->ipv6_prefix; // // Length of payload (not header) p_ip6_hdr->ip6_plen = htons(sizeof(*pinfo) + sizeof(*p_nra)); diff --git a/l2tpns.c b/l2tpns.c index cc69c2d..dd1e8ae 100644 --- a/l2tpns.c +++ b/l2tpns.c @@ -102,7 +102,7 @@ union iphash { struct ipv6radix { sessionidt sess; struct ipv6radix *branch; -} ipv6_hash[256]; // Mapping from IPv6 address to session structures. +} ipv6_hash[16]; // Mapping from IPv6 address to session structures. // Traffic counters. static uint32_t udp_rx = 0, udp_rx_pkt = 0, udp_tx = 0; @@ -983,19 +983,24 @@ static sessionidt lookup_ipv6map(struct in6_addr ip) char ipv6addr[INET6_ADDRSTRLEN]; curnode = &ipv6_hash[ip.s6_addr[0]]; + curnode = &ipv6_hash[((ip.s6_addr[0]) & 0xF0)>>4]; i = 1; s = curnode->sess; - while (s == 0 && i < 15 && curnode->branch != NULL) + while (s == 0 && i < 32 && curnode->branch != NULL) { - curnode = &curnode->branch[ip.s6_addr[i]]; + if (i & 1) + curnode = &curnode->branch[ip.s6_addr[i>>1] & 0x0F]; + else + curnode = &curnode->branch[(ip.s6_addr[i>>1] & 0xF0)>>4]; + s = curnode->sess; i++; } LOG(4, s, session[s].tunnel, "Looking up address %s and got %d\n", - inet_ntop(AF_INET6, &ip, ipv6addr, - INET6_ADDRSTRLEN), + inet_ntop(AF_INET6, &ip, ipv6addr, + INET6_ADDRSTRLEN), s); return s; @@ -1036,6 +1041,19 @@ sessionidt sessionbyipv6(struct in6_addr ip) return 0; } +sessionidt sessionbyipv6new(struct in6_addr ip) +{ + sessionidt s; + CSTAT(sessionbyipv6new); + + s = lookup_ipv6map(ip); + + if (s > 0 && s < MAXSESSION && session[s].opened) + return s; + + return 0; +} + // // Take an IP address in HOST byte order and // add it to the sessionid by IP cache. @@ -1075,22 +1093,28 @@ static void uncache_ipmap(in_addr_t ip) static void cache_ipv6map(struct in6_addr ip, int prefixlen, sessionidt s) { int i; - int bytes; + int niblles; struct ipv6radix *curnode; char ipv6addr[INET6_ADDRSTRLEN]; - curnode = &ipv6_hash[ip.s6_addr[0]]; + curnode = &ipv6_hash[((ip.s6_addr[0]) & 0xF0)>>4]; - bytes = prefixlen >> 3; + niblles = prefixlen >> 2; i = 1; - while (i < bytes) { + + while (i < niblles) + { if (curnode->branch == NULL) { - if (!(curnode->branch = calloc(256, - sizeof (struct ipv6radix)))) + if (!(curnode->branch = calloc(16, sizeof (struct ipv6radix)))) return; } - curnode = &curnode->branch[ip.s6_addr[i]]; + + if (i & 1) + curnode = &curnode->branch[ip.s6_addr[i>>1] & 0x0F]; + else + curnode = &curnode->branch[(ip.s6_addr[i>>1] & 0xF0)>>4]; + i++; } @@ -1098,13 +1122,13 @@ static void cache_ipv6map(struct in6_addr ip, int prefixlen, sessionidt s) if (s > 0) LOG(4, s, session[s].tunnel, "Caching ip address %s/%d\n", - inet_ntop(AF_INET6, &ip, ipv6addr, - INET6_ADDRSTRLEN), + inet_ntop(AF_INET6, &ip, ipv6addr, + INET6_ADDRSTRLEN), prefixlen); else if (s == 0) LOG(4, 0, 0, "Un-caching ip address %s/%d\n", - inet_ntop(AF_INET6, &ip, ipv6addr, - INET6_ADDRSTRLEN), + inet_ntop(AF_INET6, &ip, ipv6addr, + INET6_ADDRSTRLEN), prefixlen); } @@ -1155,7 +1179,6 @@ int cmd_show_ipcache(struct cli_def *cli, const char *command, char **argv, int return CLI_OK; } - // Find session by username, 0 for not found // walled garden users aren't authenticated, so the username is // reasonably useless. Ignore them to avoid incorrect actions @@ -1692,7 +1715,6 @@ static void processipv6out(uint8_t * buf, int len) sessionidt s; sessiont *sp; tunnelidt t; - in_addr_t ip; struct in6_addr ip6; uint8_t *data = buf; // Keep a copy of the originals. @@ -1731,10 +1753,9 @@ static void processipv6out(uint8_t * buf, int len) if (s == 0) { - ip = *(uint32_t *)(buf + 32); - s = sessionbyip(ip); + s = sessionbyipv6new(ip6); } - + if (s == 0) { // Is this a packet for a session that doesn't exist? diff --git a/l2tpns.h b/l2tpns.h index 80a8dbb..fdfaa62 100644 --- a/l2tpns.h +++ b/l2tpns.h @@ -606,6 +606,7 @@ struct Tstats uint32_t call_processudp; uint32_t call_sessionbyip; uint32_t call_sessionbyipv6; + uint32_t call_sessionbyipv6new; uint32_t call_sessionbyuser; uint32_t call_sendarp; uint32_t call_sendipcp; @@ -951,6 +952,7 @@ void send_ipv6_ra(sessionidt s, tunnelidt t, struct in6_addr *ip); void route6set(sessionidt s, struct in6_addr ip, int prefixlen, int add); sessionidt sessionbyip(in_addr_t ip); sessionidt sessionbyipv6(struct in6_addr ip); +sessionidt sessionbyipv6new(struct in6_addr ip); sessionidt sessionbyuser(char *username); void increment_counter(uint32_t *counter, uint32_t *wrap, uint32_t delta); void random_data(uint8_t *buf, int len); diff --git a/ppp.c b/ppp.c index 6bcb8f0..21f5985 100644 --- a/ppp.c +++ b/ppp.c @@ -1580,8 +1580,16 @@ void processipv6cp(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) gotip++; // seen address if (o[1] != 10) return; - ident[0] = htonl(session[s].ip); - ident[1] = 0; + if (session[s].ipv6address.s6_addr[0]) + { + // LSB 64bits of assigned IPv6 address to user (see radius attribut Framed-IPv6-Address) + memcpy(&ident[0], &session[s].ipv6address.s6_addr[8], 8); + } + else + { + ident[0] = htonl(session[s].ip); + ident[1] = 0; + } if (memcmp(o + 2, ident, sizeof(ident))) { @@ -2254,7 +2262,18 @@ void processipv6in(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l) return; // no spoof - if ((ipv4 != session[s].ip || memcmp(&config->ipv6_prefix, &ip, 8)) && sessionbyipv6(ip) != s) + if (session[s].ipv6address.s6_addr[0]) + { + if ((sessionbyipv6new(ip) != s) && + (ip.s6_addr[0] != 0xFE || ip.s6_addr[1] != 0x80 || ip.s6_addr16[1] != 0 || ip.s6_addr16[2] != 0 || ip.s6_addr16[3] != 0)) + { + char str[INET6_ADDRSTRLEN]; + LOG(5, s, t, "Dropping packet with spoofed IP %s\n", + inet_ntop(AF_INET6, &ip, str, INET6_ADDRSTRLEN)); + return; + } + } + else if ((ipv4 != session[s].ip || memcmp(&config->ipv6_prefix, &ip, 8)) && sessionbyipv6(ip) != s) { char str[INET6_ADDRSTRLEN]; LOG(5, s, t, "Dropping packet with spoofed IP %s\n", -- 2.20.1