Add DHCPv6 support with acceleration
This commit is contained in:
parent
a9e18411d3
commit
5dcbd68b75
5 changed files with 164 additions and 5 deletions
137
dhcp6.c
137
dhcp6.c
|
|
@ -4,13 +4,21 @@
|
||||||
* GPL licenced
|
* GPL licenced
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#define _GNU_SOURCE
|
||||||
#include <netinet/icmp6.h>
|
#include <netinet/icmp6.h>
|
||||||
#include <netinet/ip6.h>
|
#include <netinet/ip6.h>
|
||||||
#include <netinet/udp.h>
|
#include <netinet/udp.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
#include "dhcp6.h"
|
#include "dhcp6.h"
|
||||||
#include "l2tpns.h"
|
#include "l2tpns.h"
|
||||||
#include "ipv6_u.h"
|
#include "ipv6_u.h"
|
||||||
|
#include "cluster.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
int dhcpv6fd;
|
||||||
|
|
||||||
struct dhcp6_in_option
|
struct dhcp6_in_option
|
||||||
{
|
{
|
||||||
|
|
@ -538,7 +546,7 @@ void dhcpv6_process_from_ipv6(sessionidt s, tunnelidt t, uint8_t *p, uint16_t l)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (p_ip6_hdr_in->ip6_nxt != 17)
|
if (p_ip6_hdr_in->ip6_nxt != IPPROTO_UDP)
|
||||||
{
|
{
|
||||||
LOG(5, 0, 0, "not UDP DHCP packet??\n");
|
LOG(5, 0, 0, "not UDP DHCP packet??\n");
|
||||||
return;
|
return;
|
||||||
|
|
@ -597,6 +605,8 @@ static int dhcpv6_format_dns_search_name(const char *strdns, uint8_t *buffer)
|
||||||
void dhcpv6_init(void)
|
void dhcpv6_init(void)
|
||||||
{
|
{
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
|
int on = 1;
|
||||||
|
struct sockaddr_in6 addr;
|
||||||
|
|
||||||
dhcp6_local_serverid.opt_hdr.code = htons(D6_OPT_SERVERID);
|
dhcp6_local_serverid.opt_hdr.code = htons(D6_OPT_SERVERID);
|
||||||
dhcp6_local_serverid.opt_hdr.len = htons(4 + sizeof(id));
|
dhcp6_local_serverid.opt_hdr.len = htons(4 + sizeof(id));
|
||||||
|
|
@ -609,4 +619,129 @@ void dhcpv6_init(void)
|
||||||
id = htobe32(0xFDFDFAFA);
|
id = htobe32(0xFDFDFAFA);
|
||||||
|
|
||||||
memcpy(dhcp6_local_serverid.duid.u.ll.addr, &id, sizeof(id));
|
memcpy(dhcp6_local_serverid.duid.u.ll.addr, &id, sizeof(id));
|
||||||
|
|
||||||
|
dhcpv6fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
|
if (dhcpv6fd < 0)
|
||||||
|
LOG(1, 0, 0, "DHCPv6: could not create UDP socket: %s\n", strerror(errno));
|
||||||
|
|
||||||
|
#ifdef SO_REUSEPORT
|
||||||
|
if (setsockopt(dhcpv6fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0)
|
||||||
|
LOG(1, 0, 0, "DHCPv6: could not set reusing port: %s\n", strerror(errno));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (setsockopt(dhcpv6fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
|
||||||
|
LOG(1, 0, 0, "DHCPv6: could not set reusing address: %s\n", strerror(errno));
|
||||||
|
|
||||||
|
#ifdef IPV6_RECVPKTINFO
|
||||||
|
if (setsockopt(dhcpv6fd, SOL_IPV6, IPV6_RECVPKTINFO, &on, sizeof(on)) < 0)
|
||||||
|
LOG(1, 0, 0, "DHCPv6: could not request pktinfo: %s\n", strerror(errno));
|
||||||
|
#else
|
||||||
|
if (setsockopt(dhcpv6fd, SOL_IPV6, IPV6_PKTINFO, &on, sizeof(on)) < 0)
|
||||||
|
LOG(1, 0, 0, "DHCPv6: could not request pktinfo: %s\n", strerror(errno));
|
||||||
|
#endif
|
||||||
|
#ifdef IPV6_V6ONLY
|
||||||
|
if (setsockopt(dhcpv6fd, SOL_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0)
|
||||||
|
LOG(1, 0, 0, "DHCPv6: could not set v6only: %s\n", strerror(errno));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memset(&addr, 0, sizeof(addr));
|
||||||
|
addr.sin6_family = AF_INET6;
|
||||||
|
addr.sin6_port = htons(DHCP6_SERVER_PORT);
|
||||||
|
if (bind(dhcpv6fd, (struct sockaddr *) &addr, sizeof(addr)) < 0)
|
||||||
|
LOG(1, 0, 0, "DHCPv6: could not bind to DHCPv6 server port\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// A new ppp interface was created, watch for DHCPv6 on it
|
||||||
|
void dhcpv6_listen(int ifidx)
|
||||||
|
{
|
||||||
|
struct ipv6_mreq mreq;
|
||||||
|
|
||||||
|
memset(&mreq, 0, sizeof(mreq));
|
||||||
|
mreq.ipv6mr_interface = ifidx;
|
||||||
|
inet_pton(AF_INET6, DHCP6_SERVER_ADDRESS, &mreq.ipv6mr_multiaddr);
|
||||||
|
if (setsockopt(dhcpv6fd, SOL_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0)
|
||||||
|
LOG(2, 0, 0, "DHCPv6: could not join DHCPv6 group: %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// A DHCPv6 request was received on a ppp interface, receive it
|
||||||
|
void dhcpv6_process_from_kernel(uint8_t *p, size_t size_bufp)
|
||||||
|
{
|
||||||
|
struct sockaddr_storage fromaddr;
|
||||||
|
struct sockaddr_in6 *sin6;
|
||||||
|
socklen_t fromlen = sizeof(fromaddr);
|
||||||
|
struct in6_addr toaddr;
|
||||||
|
int ifidx;
|
||||||
|
int r, s, t;
|
||||||
|
|
||||||
|
r = recvfromto6(dhcpv6fd, p, size_bufp, 0, (struct sockaddr *) &fromaddr, &fromlen, &toaddr, &ifidx);
|
||||||
|
if (r < 0)
|
||||||
|
{
|
||||||
|
static time_t lastwarn;
|
||||||
|
time_t now = time(NULL);
|
||||||
|
if (now > lastwarn)
|
||||||
|
{
|
||||||
|
LOG(5, 0, 0, "DHCPV6: reception error: %s\n", strerror(errno));
|
||||||
|
lastwarn = now;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LOG(5, 0, 0, "Got packet on DHCP socket on if %d\n", ifidx);
|
||||||
|
|
||||||
|
if (fromaddr.ss_family != AF_INET6)
|
||||||
|
{
|
||||||
|
LOG(5, 0, 0, "DHCPV6: got strange family %d\n", fromaddr.ss_family);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sin6 = (struct sockaddr_in6 *) &fromaddr;
|
||||||
|
|
||||||
|
if (ntohs(sin6->sin6_port) != DHCP6_CLIENT_PORT)
|
||||||
|
{
|
||||||
|
LOG(5, 0, 0, "DHCPV6: got strange client port %d\n", ntohs(sin6->sin6_port));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (s = 1; s < MAXSESSION; s++)
|
||||||
|
{
|
||||||
|
if (sess_local[s].ppp_if_idx != ifidx)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
t = session[s].tunnel;
|
||||||
|
|
||||||
|
if (config->cluster_iam_master)
|
||||||
|
dhcpv6_process(s, t, &sin6->sin6_addr, p, r);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// DHCPV6 must be managed by the Master.
|
||||||
|
|
||||||
|
// Fake UDPv6 header
|
||||||
|
struct udphdr *udp = (struct udphdr *)p - 1;
|
||||||
|
udp->source = sin6->sin6_port;
|
||||||
|
udp->dest = htons(DHCP6_SERVER_PORT);
|
||||||
|
udp->len = sizeof(*udp) + r;
|
||||||
|
// udp->check is not checked by Master anyway
|
||||||
|
r += sizeof(*udp);
|
||||||
|
|
||||||
|
struct ip6_hdr *ip6 = (struct ip6_hdr *)udp - 1;
|
||||||
|
ip6->ip6_flow = htonl(6);
|
||||||
|
ip6->ip6_plen = htons(r);
|
||||||
|
ip6->ip6_nxt = IPPROTO_UDP;
|
||||||
|
ip6->ip6_hlim = 255;
|
||||||
|
memcpy(&ip6->ip6_src, &sin6->sin6_addr, sizeof(sin6->sin6_addr));
|
||||||
|
memcpy(&ip6->ip6_dst, &toaddr, sizeof(toaddr));
|
||||||
|
r += sizeof(*ip6);
|
||||||
|
|
||||||
|
uint16_t *w = (uint16_t *)ip6 - 4;
|
||||||
|
w[0] = htons(0x0002); /* L2TP data*/
|
||||||
|
w[1] = htons(t);
|
||||||
|
w[2] = htons(s);
|
||||||
|
w[3] = htons(PPPIPV6); /* PPP protocol */
|
||||||
|
r += 8;
|
||||||
|
|
||||||
|
master_forward_packet((uint8_t *) w, r, htonl(tunnel[t].ip), htons(tunnel[t].port), tunnel[t].indexudp);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
7
dhcp6.h
7
dhcp6.h
|
|
@ -7,6 +7,10 @@
|
||||||
#ifndef __DHCP6_H__
|
#ifndef __DHCP6_H__
|
||||||
#define __DHCP6_H__
|
#define __DHCP6_H__
|
||||||
|
|
||||||
|
#define DHCP6_CLIENT_PORT 546
|
||||||
|
#define DHCP6_SERVER_PORT 547
|
||||||
|
#define DHCP6_SERVER_ADDRESS "ff02::1:2"
|
||||||
|
|
||||||
#define DHCP6_SOLICIT 1
|
#define DHCP6_SOLICIT 1
|
||||||
#define DHCP6_ADVERTISE 2
|
#define DHCP6_ADVERTISE 2
|
||||||
#define DHCP6_REQUEST 3
|
#define DHCP6_REQUEST 3
|
||||||
|
|
@ -212,7 +216,10 @@ struct dhcp6_opt_ia_prefix {
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
// dhcp6.c
|
// dhcp6.c
|
||||||
|
extern int dhcpv6fd;
|
||||||
void dhcpv6_process_from_ipv6(uint16_t s, uint16_t t, uint8_t *p, uint16_t l);
|
void dhcpv6_process_from_ipv6(uint16_t s, uint16_t t, uint8_t *p, uint16_t l);
|
||||||
void dhcpv6_init(void);
|
void dhcpv6_init(void);
|
||||||
|
void dhcpv6_listen(int ifidx);
|
||||||
|
void dhcpv6_process_from_kernel(uint8_t *p, size_t size_bufp);
|
||||||
|
|
||||||
#endif /* __DHCP6_H__ */
|
#endif /* __DHCP6_H__ */
|
||||||
|
|
|
||||||
23
l2tpns.c
23
l2tpns.c
|
|
@ -1228,6 +1228,8 @@ static int create_kernel_accel(sessionidt s)
|
||||||
sess_local[s].ppp_if_fd = ppp_if_fd;
|
sess_local[s].ppp_if_fd = ppp_if_fd;
|
||||||
sess_local[s].ppp_if_idx = ifr.ifr_ifindex;
|
sess_local[s].ppp_if_idx = ifr.ifr_ifindex;
|
||||||
|
|
||||||
|
dhcpv6_listen(ifr.ifr_ifindex);
|
||||||
|
|
||||||
memset(&sess_local[s].last_stats, 0, sizeof(sess_local[s].last_stats));
|
memset(&sess_local[s].last_stats, 0, sizeof(sess_local[s].last_stats));
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -5447,8 +5449,8 @@ static int still_busy(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the base set of fds polled: cli, cluster, tun, udp (MAX_UDPFD), control, dae, netlink, udplac, pppoedisc, pppoesess, kernel ppp
|
// the base set of fds polled: cli, cluster, tun, udp (MAX_UDPFD), control, dae, netlink, udplac, pppoedisc, pppoesess, dhcpv6
|
||||||
#define BASE_FDS (9 + MAX_UDPFD)
|
#define BASE_FDS (10 + MAX_UDPFD)
|
||||||
|
|
||||||
// additional polled fds
|
// additional polled fds
|
||||||
#ifdef BGP
|
#ifdef BGP
|
||||||
|
|
@ -5464,14 +5466,17 @@ static int still_busy(void)
|
||||||
|
|
||||||
#define MAX_FDS (BASE_FDS + RADIUS_FDS + EXTRA_FDS + L2TP_FDS + PPPOX_FDS + PPP_CHAN_FDS + PPP_IF_FDS)
|
#define MAX_FDS (BASE_FDS + RADIUS_FDS + EXTRA_FDS + L2TP_FDS + PPPOX_FDS + PPP_CHAN_FDS + PPP_IF_FDS)
|
||||||
|
|
||||||
|
// for the header of the forwarded MPPP/DHCP packet (see C_MPPP_FORWARD)
|
||||||
|
#define SLACK 56
|
||||||
|
|
||||||
// main loop - gets packets on tun or udp and processes them
|
// main loop - gets packets on tun or udp and processes them
|
||||||
static void mainloop(void)
|
static void mainloop(void)
|
||||||
{
|
{
|
||||||
int i, j;
|
int i, j;
|
||||||
uint8_t buf[65536];
|
uint8_t buf[65536];
|
||||||
uint8_t *p = buf + 32; // for the header of the forwarded MPPP packet (see C_MPPP_FORWARD)
|
uint8_t *p = buf + SLACK; // for the header of the forwarded MPPP packet (see C_MPPP_FORWARD)
|
||||||
// and the forwarded pppoe session
|
// and the forwarded pppoe session
|
||||||
int size_bufp = sizeof(buf) - 32;
|
int size_bufp = sizeof(buf) - SLACK;
|
||||||
clockt next_cluster_ping = 0; // send initial ping immediately
|
clockt next_cluster_ping = 0; // send initial ping immediately
|
||||||
struct epoll_event events[MAX_FDS];
|
struct epoll_event events[MAX_FDS];
|
||||||
int maxevent = sizeof(events)/sizeof(*events);
|
int maxevent = sizeof(events)/sizeof(*events);
|
||||||
|
|
@ -5528,6 +5533,10 @@ static void mainloop(void)
|
||||||
e.data.ptr = &d[i++];
|
e.data.ptr = &d[i++];
|
||||||
epoll_ctl(epollfd, EPOLL_CTL_ADD, pppoesessfd, &e);
|
epoll_ctl(epollfd, EPOLL_CTL_ADD, pppoesessfd, &e);
|
||||||
|
|
||||||
|
d[i].type = FD_TYPE_DHCPV6;
|
||||||
|
e.data.ptr = &d[i++];
|
||||||
|
epoll_ctl(epollfd, EPOLL_CTL_ADD, dhcpv6fd, &e);
|
||||||
|
|
||||||
for (j = 0; j < config->nbudpfd; j++)
|
for (j = 0; j < config->nbudpfd; j++)
|
||||||
{
|
{
|
||||||
d[i].type = FD_TYPE_UDP;
|
d[i].type = FD_TYPE_UDP;
|
||||||
|
|
@ -5787,6 +5796,12 @@ static void mainloop(void)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case FD_TYPE_DHCPV6:
|
||||||
|
{
|
||||||
|
dhcpv6_process_from_kernel(p, size_bufp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
LOG(0, 0, 0, "Unexpected fd type returned from epoll_wait: %d\n", d->type);
|
LOG(0, 0, 0, "Unexpected fd type returned from epoll_wait: %d\n", d->type);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
l2tpns.h
1
l2tpns.h
|
|
@ -1086,6 +1086,7 @@ struct event_data {
|
||||||
FD_TYPE_PPPOX,
|
FD_TYPE_PPPOX,
|
||||||
FD_TYPE_PPP_CHAN,
|
FD_TYPE_PPP_CHAN,
|
||||||
FD_TYPE_PPP_IF,
|
FD_TYPE_PPP_IF,
|
||||||
|
FD_TYPE_DHCPV6,
|
||||||
} type;
|
} type;
|
||||||
int index; // for RADIUS, BGP, UDP
|
int index; // for RADIUS, BGP, UDP
|
||||||
};
|
};
|
||||||
|
|
|
||||||
1
util.c
1
util.c
|
|
@ -139,6 +139,7 @@ pid_t fork_and_close()
|
||||||
if (sess_local[i].ppp_if_fd >= 0) close(sess_local[i].ppp_if_fd);
|
if (sess_local[i].ppp_if_fd >= 0) close(sess_local[i].ppp_if_fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dhcpv6fd != -1) close(dhcpv6fd);
|
||||||
if (snoopfd != -1) close(snoopfd);
|
if (snoopfd != -1) close(snoopfd);
|
||||||
if (rand_fd != -1) close(rand_fd);
|
if (rand_fd != -1) close(rand_fd);
|
||||||
if (epollfd != -1) close(epollfd);
|
if (epollfd != -1) close(epollfd);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue