提交 046642d5 编写于 作者: K Kozlov Dmitry

ipoe: integrating accel-pppd and ipoe kernel module

上级 7c245028
......@@ -69,6 +69,7 @@ ADD_EXECUTABLE(accel-pppd
libnetlink/libnetlink.c
libnetlink/iplink.c
libnetlink/genl.c
pwdb.c
ipdb.c
......
......@@ -4,6 +4,7 @@ SET(sources
ipoe.c
dhcpv4.c
dhcpv4_options.c
ipoe_netlink.c
)
IF (LUA)
......
......@@ -109,7 +109,7 @@ struct dhcpv4_serv *dhcpv4_create(struct triton_context_t *ctx, const char *ifna
}
fcntl(raw_sock, F_SETFL, O_NONBLOCK);
fcntl(raw_sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC);
fcntl(raw_sock, F_SETFD, fcntl(raw_sock, F_GETFD) | FD_CLOEXEC);
fcntl(sock, F_SETFL, O_NONBLOCK);
fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC);
......
../../../drivers/ipoe/ipoe.h
\ No newline at end of file
......@@ -8,6 +8,8 @@
#include <time.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/if.h>
......@@ -49,6 +51,7 @@ static int conf_netmask = 24;
static int conf_lease_time = 600;
static int conf_lease_timeout = 660;
static int conf_verbose;
static int conf_opt_single = 0;
static unsigned int stat_starting;
static unsigned int stat_active;
......@@ -153,14 +156,37 @@ static void ipoe_session_start(struct ipoe_session *ses)
{
int r;
char *passwd;
struct ifreq ifr;
if (ses->serv->opt_single)
strncpy(ses->ses.ifname, ses->serv->ifname, AP_IFNAME_LEN);
else {
ses->ifindex = ipoe_nl_create(0, 0, ses->serv->ifname, ses->hwaddr);
if (ses->ifindex == -1) {
log_ppp_error("ipoe: failed to create interface\n");
ipoe_session_finished(&ses->ses);
return;
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_ifindex = ses->ifindex;
if (ioctl(sock_fd, SIOCGIFNAME, &ifr, sizeof(ifr))) {
log_ppp_error("ipoe: failed to get interface name\n");
ses->ifindex = -1;
ipoe_session_finished(&ses->ses);
return;
}
strncpy(ses->ses.ifname, ifr.ifr_name, AP_IFNAME_LEN);
}
ipoe_session_set_username(ses);
if (!ses->ses.username) {
ipoe_session_finished(&ses->ses);
return;
ipoe_session_set_username(ses);
if (!ses->ses.username) {
ipoe_session_finished(&ses->ses);
return;
}
}
triton_event_fire(EV_CTRL_STARTING, &ses->ses);
......@@ -186,44 +212,46 @@ static void ipoe_session_start(struct ipoe_session *ses)
return;
}
if (ses->dhcpv4_request) {
ses->ses.ipv4 = ipdb_get_ipv4(&ses->ses);
if (!ses->ses.ipv4) {
log_ppp_warn("no free IPv4 address\n");
ap_session_terminate(&ses->ses, TERM_AUTH_ERROR, 0);
return;
}
ses->ses.ipv4 = ipdb_get_ipv4(&ses->ses);
if (!ses->ses.ipv4) {
log_ppp_warn("no free IPv4 address\n");
ap_session_terminate(&ses->ses, TERM_AUTH_ERROR, 0);
return;
}
if (conf_gw_address)
ses->ses.ipv4->addr = conf_gw_address;
if (conf_netmask)
ses->ses.ipv4->mask = conf_netmask;
else if (!ses->ses.ipv4->mask)
ses->ses.ipv4->mask = 24;
if (conf_gw_address)
ses->ses.ipv4->addr = conf_gw_address;
if (conf_netmask)
ses->ses.ipv4->mask = conf_netmask;
else if (!ses->ses.ipv4->mask)
ses->ses.ipv4->mask = 24;
if (ses->dhcpv4_request) {
dhcpv4_send_reply(DHCPOFFER, ses->serv->dhcpv4, ses->dhcpv4_request, &ses->ses, conf_lease_time);
dhcpv4_packet_free(ses->dhcpv4_request);
ses->dhcpv4_request = NULL;
ses->timer.expire = ipoe_session_timeout;
ses->timer.expire_tv.tv_sec = conf_offer_timeout;
triton_timer_add(&ses->ctx, &ses->timer, 0);
}
ses->timer.expire = ipoe_session_timeout;
ses->timer.expire_tv.tv_sec = conf_offer_timeout;
triton_timer_add(&ses->ctx, &ses->timer, 0);
}
static void ipoe_session_activate(struct ipoe_session *ses)
{
ap_session_activate(&ses->ses);
if (ses->ses.state == AP_STATE_ACTIVE)
dhcpv4_send_reply(DHCPACK, ses->serv->dhcpv4, ses->dhcpv4_request, &ses->ses, conf_lease_time);
else
dhcpv4_send_nak(ses->serv->dhcpv4, ses->dhcpv4_request);
if (ses->dhcpv4_request) {
if (ses->ses.state == AP_STATE_ACTIVE)
dhcpv4_send_reply(DHCPACK, ses->serv->dhcpv4, ses->dhcpv4_request, &ses->ses, conf_lease_time);
else
dhcpv4_send_nak(ses->serv->dhcpv4, ses->dhcpv4_request);
dhcpv4_packet_free(ses->dhcpv4_request);
ses->dhcpv4_request = NULL;
dhcpv4_packet_free(ses->dhcpv4_request);
ses->dhcpv4_request = NULL;
}
}
static void ipoe_session_keepalive(struct ipoe_session *ses)
......@@ -262,10 +290,19 @@ static void ipoe_session_free(struct ipoe_session *ses)
if (ses->dhcpv4_request)
dhcpv4_packet_free(ses->dhcpv4_request);
if (ses->ctrl.called_station_id)
_free(ses->ctrl.called_station_id);
if (ses->ctrl.calling_station_id)
_free(ses->ctrl.calling_station_id);
triton_context_unregister(&ses->ctx);
if (ses->data)
_free(ses->data);
if (ses->ifindex != -1)
ipoe_nl_delete(ses->ifindex);
mempool_free(ses);
}
......@@ -299,7 +336,7 @@ static void ipoe_session_close(struct triton_context_t *ctx)
ipoe_session_finished(&ses->ses);
}
static struct ipoe_session *ipoe_session_create(struct ipoe_serv *serv, struct dhcpv4_packet *pack)
static struct ipoe_session *ipoe_session_create_dhcpv4(struct ipoe_serv *serv, struct dhcpv4_packet *pack)
{
struct ipoe_session *ses;
int dlen = 0;
......@@ -316,6 +353,7 @@ static struct ipoe_session *ipoe_session_create(struct ipoe_serv *serv, struct d
ap_session_init(&ses->ses);
ses->serv = serv;
ses->ifindex = -1;
ses->dhcpv4_request = pack;
ses->xid = pack->hdr->xid;
......@@ -372,7 +410,7 @@ static struct ipoe_session *ipoe_session_create(struct ipoe_serv *serv, struct d
ses->ctrl.name = "ipoe";
ses->ctrl.calling_station_id = _malloc(19);
ses->ctrl.called_station_id = serv->ifname;
ses->ctrl.called_station_id = _strdup(serv->ifname);
ptr = ses->hwaddr;
sprintf(ses->ctrl.calling_station_id, "%02x:%02x:%02x:%02x:%02x:%02x",
......@@ -394,7 +432,7 @@ static struct ipoe_session *ipoe_session_create(struct ipoe_serv *serv, struct d
return ses;
}
static void ipoe_dhcpv4_recv(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *pack)
static void ipoe_recv_dhcpv4(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *pack)
{
struct ipoe_serv *serv = container_of(dhcpv4->ctx, typeof(*serv), ctx);
struct ipoe_session *ses;
......@@ -404,7 +442,7 @@ static void ipoe_dhcpv4_recv(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *p
if (pack->msg_type == DHCPDISCOVER) {
ses = ipoe_session_lookup(serv, pack);
if (!ses) {
ses = ipoe_session_create(serv, pack);
ses = ipoe_session_create_dhcpv4(serv, pack);
if (conf_verbose && ses) {
log_switch(dhcpv4->ctx, &ses->ses);
......@@ -487,6 +525,80 @@ static void ipoe_dhcpv4_recv(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *p
pthread_mutex_unlock(&serv->lock);
}
static struct ipoe_session *ipoe_session_create_up(struct ipoe_serv *serv, struct ethhdr *eth, struct iphdr *iph)
{
struct ipoe_session *ses;
ses = mempool_alloc(ses_pool);
if (!ses) {
log_emerg("out of memery\n");
return NULL;
}
memset(ses, 0, sizeof(*ses));
ap_session_init(&ses->ses);
ses->serv = serv;
ses->ifindex = -1;
memcpy(ses->hwaddr, eth->h_source, 6);
ses->ctx.before_switch = log_switch;
ses->ctx.close = ipoe_session_close;
ses->ctrl.ctx = &ses->ctx;
ses->ctrl.started = ipoe_session_started;
ses->ctrl.finished = ipoe_session_finished;
ses->ctrl.terminate = ipoe_session_terminate;
ses->ctrl.type = CTRL_TYPE_IPOE;
ses->ctrl.name = "ipoe";
ses->ctrl.calling_station_id = _malloc(17);
ses->ctrl.called_station_id = _malloc(17);
u_inet_ntoa(iph->saddr, ses->ctrl.calling_station_id);
u_inet_ntoa(iph->daddr, ses->ctrl.called_station_id);
ses->ses.username = _strdup(ses->ctrl.calling_station_id);
ses->ses.ctrl = &ses->ctrl;
ses->ses.chan_name = ses->ctrl.calling_station_id;
triton_context_register(&ses->ctx, &ses->ses);
triton_context_wakeup(&ses->ctx);
//pthread_mutex_lock(&serv->lock);
list_add_tail(&ses->entry, &serv->sessions);
//pthread_mutex_unlock(&serv->lock);
triton_context_call(&ses->ctx, (triton_event_func)ipoe_session_start, ses);
return ses;
}
void ipoe_recv_up(int ifindex, struct ethhdr *eth, struct iphdr *iph)
{
struct ipoe_serv *serv;
struct ipoe_session *ses;
list_for_each_entry(serv, &serv_list, entry) {
if (serv->ifindex != ifindex)
continue;
pthread_mutex_lock(&serv->lock);
list_for_each_entry(ses, &serv->sessions, entry) {
if (memcmp(ses->hwaddr, eth->h_source, 6) == 0) {
pthread_mutex_unlock(&serv->lock);
return;
}
}
pthread_mutex_unlock(&serv->lock);
ipoe_session_create_up(serv, eth, iph);
}
}
static void ipoe_serv_close(struct triton_context_t *ctx)
{
struct ipoe_serv *serv = container_of(ctx, typeof(*serv), ctx);
......@@ -546,9 +658,15 @@ static void add_interface(const char *ifname, int ifindex, const char *opt)
if (ptr[7] && ptr[7] != ',')
goto out_err_parse;
opt_single = 1;
} else
opt_single = 0;
} else {
ptr = strstr(opt, ",shared");
if (ptr) {
if (ptr[7] && ptr[7] != ',')
goto out_err_parse;
opt_single = 0;
} else
opt_single = conf_opt_single;
}
list_for_each_entry(serv, &serv_list, entry) {
if (strcmp(ifname, serv->ifname) == 0) {
......@@ -567,6 +685,7 @@ static void add_interface(const char *ifname, int ifindex, const char *opt)
serv->ifindex = ifindex;
serv->opt_single = opt_single;
serv->opt_dhcpv4 = conf_dhcpv4;
serv->active = 1;
INIT_LIST_HEAD(&serv->sessions);
pthread_mutex_init(&serv->lock, NULL);
......@@ -575,11 +694,13 @@ static void add_interface(const char *ifname, int ifindex, const char *opt)
if (serv->opt_dhcpv4) {
serv->dhcpv4 = dhcpv4_create(&serv->ctx, serv->ifname);
if (serv->dhcpv4)
serv->dhcpv4->recv = ipoe_dhcpv4_recv;
serv->dhcpv4->recv = ipoe_recv_dhcpv4;
}
triton_context_wakeup(&serv->ctx);
list_add_tail(&serv->entry, &serv_list);
return;
out_err_parse:
......@@ -678,6 +799,56 @@ static void load_interfaces(struct conf_sect_t *sect)
}
}
static void parse_local_net(const char *opt)
{
const char *ptr;
char str[17];
in_addr_t addr;
int mask;
char *endptr;
ptr = strchr(opt, '/');
if (ptr) {
memcpy(str, opt, ptr - opt);
str[ptr - opt] = 0;
addr = inet_addr(str);
if (addr == INADDR_NONE)
goto out_err;
mask = strtoul(ptr + 1, &endptr, 10);
if (mask > 32)
goto out_err;
} else {
addr = inet_addr(opt);
if (addr == INADDR_NONE)
goto out_err;
mask = 24;
}
mask = (1 << mask) - 1;
ipoe_nl_add_net(addr & mask, mask);
return;
out_err:
log_error("ipoe: failed to parse 'local-net=%s'\n", opt);
}
static void load_local_nets(struct conf_sect_t *sect)
{
struct conf_option_t *opt;
ipoe_nl_delete_nets();
list_for_each_entry(opt, &sect->items, entry) {
if (strcmp(opt->name, "local-net"))
continue;
if (!opt->val)
continue;
parse_local_net(opt->val);
}
}
static void load_config(void)
{
const char *opt;
......@@ -687,6 +858,7 @@ static void load_config(void)
return;
load_interfaces(s);
load_local_nets(s);
opt = conf_get_opt("ipoe", "username");
if (opt) {
......
......@@ -44,11 +44,22 @@ struct ipoe_session
uint32_t giaddr;
uint8_t *data;
struct dhcpv4_packet *dhcpv4_request;
int ifindex;
};
#ifdef USE_LUA
int ipoe_lua_set_username(struct ipoe_session *, const char *func);
#endif
struct iphdr;
struct ethhdr;
void ipoe_recv_up(int ifindex, struct ethhdr *eth, struct iphdr *iph);
void ipoe_nl_add_net(uint32_t addr, int mask);
void ipoe_nl_delete_nets(void);
int ipoe_nl_create(uint32_t peer_addr, uint32_t addr, const char *ifname, uint8_t *hwaddr);
void ipoe_nl_delete(int ifindex);
#endif
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <linux/if.h>
#include <linux/genetlink.h>
#include "triton.h"
#include "log.h"
#include "genl.h"
#include "libnetlink.h"
#include "ipoe.h"
#include "if_ipoe.h"
#define PKT_ATTR_MAX 256
static struct rtnl_handle rth;
static struct triton_md_handler_t up_hnd;
static int ipoe_genl_id;
void ipoe_nl_delete_nets(void)
{
struct nlmsghdr *nlh;
struct genlmsghdr *ghdr;
struct {
struct nlmsghdr n;
char buf[1024];
} req;
if (rth.fd == -1)
return;
nlh = &req.n;
nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nlh->nlmsg_type = ipoe_genl_id;
ghdr = NLMSG_DATA(&req.n);
ghdr->cmd = IPOE_CMD_DEL_NET;
addattr32(nlh, 1024, IPOE_ATTR_ADDR, 0);
if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL, 0) < 0 )
log_error("ipoe: nl_del_net: error talking to kernel\n");
}
void ipoe_nl_add_net(uint32_t addr, int mask)
{
struct nlmsghdr *nlh;
struct genlmsghdr *ghdr;
struct {
struct nlmsghdr n;
char buf[1024];
} req;
if (rth.fd == -1)
return;
nlh = &req.n;
nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nlh->nlmsg_type = ipoe_genl_id;
ghdr = NLMSG_DATA(&req.n);
ghdr->cmd = IPOE_CMD_ADD_NET;
addattr32(nlh, 1024, IPOE_ATTR_ADDR, addr);
addattr32(nlh, 1024, IPOE_ATTR_MASK, mask);
if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL, 0) < 0 )
log_error("ipoe: nl_add_net: error talking to kernel\n");
}
int ipoe_nl_create(uint32_t peer_addr, uint32_t addr, const char *ifname, uint8_t *hwaddr)
{
struct rtnl_handle rth;
struct nlmsghdr *nlh;
struct genlmsghdr *ghdr;
struct rtattr *tb[IPOE_ATTR_MAX + 1];
struct rtattr *attrs;
int len;
int ret = -1;
struct {
struct nlmsghdr n;
char buf[1024];
} req;
union {
uint8_t hwaddr[6];
uint64_t u64;
} u;
if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC)) {
log_ppp_error("ipoe: cannot open generic netlink socket\n");
return -1;
}
nlh = &req.n;
nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nlh->nlmsg_type = ipoe_genl_id;
ghdr = NLMSG_DATA(&req.n);
ghdr->cmd = IPOE_CMD_DELETE;
if (peer_addr)
addattr32(nlh, 1024, IPOE_ATTR_PEER_ADDR, peer_addr);
if (addr)
addattr32(nlh, 1024, IPOE_ATTR_ADDR, addr);
if (hwaddr) {
memcpy(u.hwaddr, hwaddr, 6);
addattr_l(nlh, 1024, IPOE_ATTR_HWADDR, &u.u64, 8);
}
if (ifname)
addattr_l(nlh, 1024, IPOE_ATTR_IFNAME, ifname, strlen(ifname) + 1);
if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL, 0) < 0 )
log_ppp_error("ipoe: nl_create: error talking to kernel\n");
if (nlh->nlmsg_type != ipoe_genl_id) {
log_ppp_error("ipoe: not a IPoE message %d\n", nlh->nlmsg_type);
goto out;
}
ghdr = NLMSG_DATA(nlh);
if (ghdr->cmd != IPOE_CMD_CREATE) {
log_ppp_error("ipoe: unknown IPoE command %d\n", ghdr->cmd);
goto out;
}
len = nlh->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);
if (len < 0) {
log_ppp_error("ipoe: wrong IPoE message len %d\n", len);
goto out;
}
attrs = (struct rtattr *)((char *)ghdr + GENL_HDRLEN);
parse_rtattr(tb, IPOE_ATTR_MAX, attrs, len);
if (!tb[IPOE_ATTR_IFINDEX]) {
log_ppp_error("ipoe: missing IPOE_ATTR_IFINDEX attribute\n");
goto out;
}
ret = *(uint32_t *)(RTA_DATA(tb[IPOE_ATTR_IFINDEX]));
out:
rtnl_close(&rth);
return ret;
}
void ipoe_nl_delete(int ifindex)
{
struct rtnl_handle rth;
struct nlmsghdr *nlh;
struct genlmsghdr *ghdr;
struct {
struct nlmsghdr n;
char buf[1024];
} req;
if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC)) {
log_ppp_error("ipoe: cannot open generic netlink socket\n");
return;
}
nlh = &req.n;
nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nlh->nlmsg_type = ipoe_genl_id;
ghdr = NLMSG_DATA(&req.n);
ghdr->cmd = IPOE_CMD_DELETE;
addattr32(nlh, 128, IPOE_ATTR_IFINDEX, ifindex);
if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL, 0) < 0 )
log_ppp_error("ipoe: nl_delete: error talking to kernel\n");
rtnl_close(&rth);
}
static void ipoe_up_handler(const struct sockaddr_nl *addr, struct nlmsghdr *h)
{
struct rtattr *tb[PKT_ATTR_MAX + 1];
struct rtattr *tb2[IPOE_ATTR_MAX + 1];
struct genlmsghdr *ghdr = NLMSG_DATA(h);
int len = h->nlmsg_len;
struct rtattr *attrs;
int i;
int ifindex;
struct iphdr *iph;
struct ethhdr *eth;
if (ghdr->cmd != IPOE_REP_PKT)
return;
len -= NLMSG_LENGTH(GENL_HDRLEN);
if (len < 0) {
log_warn("ipoe: wrong controller message length %d\n", len);
return;
}
attrs = (struct rtattr *)((char *)ghdr + GENL_HDRLEN);
parse_rtattr(tb, PKT_ATTR_MAX, attrs, len);
for (i = 1; i < PKT_ATTR_MAX; i++) {
if (!tb[i])
break;
parse_rtattr_nested(tb2, IPOE_ATTR_MAX, tb[i]);
if (!tb2[IPOE_ATTR_ETH_HDR] || !tb2[IPOE_ATTR_IP_HDR] || !tb2[IPOE_ATTR_IFINDEX])
continue;
ifindex = *(uint32_t *)(RTA_DATA(tb2[IPOE_ATTR_IFINDEX]));
iph = (struct iphdr *)(RTA_DATA(tb2[IPOE_ATTR_IP_HDR]));
eth = (struct ethhdr *)(RTA_DATA(tb2[IPOE_ATTR_ETH_HDR]));
ipoe_recv_up(ifindex, eth, iph);
}
}
static int ipoe_up_read(struct triton_md_handler_t *h)
{
int status;
struct nlmsghdr *hdr;
struct sockaddr_nl nladdr;
struct iovec iov;
struct msghdr msg = {
.msg_name = &nladdr,
.msg_namelen = sizeof(nladdr),
.msg_iov = &iov,
.msg_iovlen = 1,
};
char buf[8192];
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
nladdr.nl_pid = 0;
nladdr.nl_groups = 0;
iov.iov_base = buf;
while (1) {
iov.iov_len = sizeof(buf);
status = recvmsg(h->fd, &msg, 0);
if (status < 0) {
if (errno == EAGAIN)
break;
log_error("ipoe: netlink error: %s\n", strerror(errno));
if (errno == ENOBUFS)
continue;
return 0;
}
if (status == 0) {
log_error("ipoe: EOF on netlink\n");
return 0;
}
if (msg.msg_namelen != sizeof(nladdr)) {
log_error("ipoe: netlink sender address length == %d\n", msg.msg_namelen);
return 0;
}
for (hdr = (struct nlmsghdr*)buf; status >= sizeof(*hdr); ) {
int len = hdr->nlmsg_len;
int l = len - sizeof(*h);
if (l<0 || len>status) {
if (msg.msg_flags & MSG_TRUNC) {
log_warn("ipoe: truncated netlink message\n");
continue;
}
log_error("ipoe: malformed netlink message\n");
continue;
}
ipoe_up_handler(&nladdr, hdr);
status -= NLMSG_ALIGN(len);
hdr = (struct nlmsghdr*)((char*)hdr + NLMSG_ALIGN(len));
}
if (msg.msg_flags & MSG_TRUNC) {
log_warn("ipoe: netlink message truncated\n");
continue;
}
if (status) {
log_error("ipoe: netlink remnant of size %d\n", status);
return 0;
}
}
return 0;
}
static void ipoe_up_close(struct triton_context_t *ctx)
{
rtnl_close(&rth);
triton_md_unregister_handler(&up_hnd);
triton_context_unregister(ctx);
}
static struct triton_context_t up_ctx = {
.close = ipoe_up_close,
};
static struct triton_md_handler_t up_hnd = {
.read = ipoe_up_read,
};
static void init(void)
{
int mcg_id = genl_resolve_mcg(IPOE_GENL_NAME, IPOE_GENL_MCG_PKT, &ipoe_genl_id);
if (mcg_id == -1) {
log_warn("ipoe: unclassified packet handling is disabled\n");
rth.fd = -1;
return;
}
if (rtnl_open_byproto(&rth, 1 << (mcg_id - 1), NETLINK_GENERIC)) {
log_error("ipoe: cannot open generic netlink socket\n");
rth.fd = -1;
return;
}
fcntl(rth.fd, F_SETFL, O_NONBLOCK);
fcntl(rth.fd, F_SETFD, fcntl(rth.fd, F_GETFD) | FD_CLOEXEC);
triton_context_register(&up_ctx, NULL);
up_hnd.fd = rth.fd;
triton_md_register_handler(&up_ctx, &up_hnd);
triton_md_enable_handler(&up_hnd, MD_MODE_READ);
triton_context_wakeup(&up_ctx);
}
DEFINE_INIT(19, init);
../libnetlink/genl.h
\ No newline at end of file
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <fcntl.h>
#include <net/if_arp.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/uio.h>
#include <linux/genetlink.h>
#include "triton.h"
#include "log.h"
#include "libnetlink.h"
#define GENL_MAX_FAM_GRPS 128
int __export genl_resolve_mcg(const char *family, const char *name, int *fam_id)
{
struct rtnl_handle rth;
struct nlmsghdr *nlh;
struct genlmsghdr *ghdr;
struct rtattr *tb[CTRL_ATTR_MAX + 1];
struct rtattr *tb2[GENL_MAX_FAM_GRPS + 1];
struct rtattr *tb3[CTRL_ATTR_MCAST_GRP_MAX + 1];
struct rtattr *attrs;
int i, len, ret = -1;
struct {
struct nlmsghdr n;
char buf[4096];
} req;
if (rtnl_open_byproto(&rth, 0, NETLINK_GENERIC)) {
log_error("genl: cannot open rtnetlink\n");
return -1;
}
nlh = &req.n;
nlh->nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
nlh->nlmsg_type = GENL_ID_CTRL;
ghdr = NLMSG_DATA(&req.n);
ghdr->cmd = CTRL_CMD_GETFAMILY;
addattr_l(nlh, 128, CTRL_ATTR_FAMILY_NAME, family, strlen(family) + 1);
if (rtnl_talk(&rth, nlh, 0, 0, nlh, NULL, NULL, 0) < 0 ) {
log_error("genl: error talking to kernel\n");
goto out;
}
if (nlh->nlmsg_type != GENL_ID_CTRL) {
log_error("genl: not a controller message %d\n", nlh->nlmsg_type);
goto out;
}
ghdr = NLMSG_DATA(nlh);
if (ghdr->cmd != CTRL_CMD_NEWFAMILY) {
log_error("genl: unknown controller command %d\n", ghdr->cmd);
goto out;
}
len = nlh->nlmsg_len - NLMSG_LENGTH(GENL_HDRLEN);
if (len < 0) {
log_error("genl: wrong controller message len %d\n", len);
goto out;
}
attrs = (struct rtattr *)((char *)ghdr + GENL_HDRLEN);
parse_rtattr(tb, CTRL_ATTR_MAX, attrs, len);
if (!tb[CTRL_ATTR_FAMILY_ID]) {
log_error("genl: missing CTRL_FAMILY_ID attribute\n");
goto out;
}
if (!tb[CTRL_ATTR_MCAST_GROUPS])
goto out;
if (fam_id)
*fam_id = *(uint32_t *)(RTA_DATA(tb[CTRL_ATTR_FAMILY_ID]));
parse_rtattr_nested(tb2, GENL_MAX_FAM_GRPS, tb[CTRL_ATTR_MCAST_GROUPS]);
for (i = 1; i < GENL_MAX_FAM_GRPS; i++) {
if (tb2[i]) {
parse_rtattr_nested(tb3, CTRL_ATTR_MCAST_GRP_MAX, tb2[i]);
if (!tb3[CTRL_ATTR_MCAST_GRP_ID] || !tb3[CTRL_ATTR_MCAST_GRP_NAME])
continue;
if (strcmp(RTA_DATA(tb3[CTRL_ATTR_MCAST_GRP_NAME]), name))
continue;
ret = *(uint32_t *)(RTA_DATA(tb3[CTRL_ATTR_MCAST_GRP_ID]));
break;
}
}
out:
rtnl_close(&rth);
return ret;
}
#ifndef __GENL_H
#define __GENL_H
int genl_resolve_mcg(const char *family, const char *name, int *fam_id);
#endif
......@@ -652,7 +652,7 @@ int __export rta_addattr_l(struct rtattr *rta, int maxlen, int type,
return 0;
}
int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
int __export parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
{
memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
while (RTA_OK(rta, len)) {
......
......@@ -57,6 +57,7 @@ struct ipoe_stats
struct ipoe_session
{
struct list_head entry;
struct list_head entry2;
__be32 addr;
__be32 peer_addr;
......@@ -94,6 +95,7 @@ struct ipoe_entry_u
static struct list_head ipoe_list[HASH_BITS + 1];
static struct list_head ipoe_list1_u[HASH_BITS + 1];
static LIST_HEAD(ipoe_list2);
static LIST_HEAD(ipoe_list2_u);
static DEFINE_SEMAPHORE(ipoe_wlock);
static LIST_HEAD(ipoe_networks);
......@@ -338,6 +340,9 @@ static netdev_tx_t ipoe_xmit(struct sk_buff *skb, struct net_device *dev)
__be32 tip;*/
int noff;
if (!ses->peer_addr)
goto drop;
noff = skb_network_offset(skb);
if (skb->protocol == htons(ETH_P_IP)) {
......@@ -447,6 +452,8 @@ static int ipoe_rcv_arp(struct sk_buff *skb, struct net_device *dev, struct pack
memcpy(&sip, arp_ptr + ETH_ALEN, 4);
if (!sip)
goto drop;
//pr_info("ipoe: recv arp %08x\n", sip);
ses = ipoe_lookup(sip);
......@@ -454,9 +461,6 @@ static int ipoe_rcv_arp(struct sk_buff *skb, struct net_device *dev, struct pack
if (!ses)
goto drop;
if (!ses->dev)
goto drop_unlock;
stats = &ses->dev->stats;
if (ses->addr || skb->dev == ses->dev) {
......@@ -518,6 +522,9 @@ static int ipoe_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_t
iph = ip_hdr(skb);
if (!iph->saddr)
goto drop;
//pr_info("ipoe: recv %08x %08x\n", iph->saddr, iph->daddr);
if (!ipoe_check_network(iph->saddr))
goto drop;
......@@ -531,9 +538,6 @@ static int ipoe_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_t
//pr_info("ipoe: recv cb=%x\n", *(__u16 *)cb_ptr);
if (ses->dev)
goto drop_unlock;
stats = &ses->dev->stats;
if (skb->dev == ses->dev) {
......@@ -646,6 +650,7 @@ static void ipoe_process_queue(struct work_struct *w)
struct sk_buff *report_skb = NULL;
void *header = NULL;
struct nlattr *ns;
int id = 1;
do {
while ((skb = skb_dequeue(&ipoe_queue))) {
......@@ -680,9 +685,12 @@ static void ipoe_process_queue(struct work_struct *w)
}
if (report_skb) {
ns = nla_nest_start(report_skb, IPOE_ATTR_PKT);
ns = nla_nest_start(report_skb, id++);
if (!ns)
goto nl_err;
if (nla_put_u32(report_skb, IPOE_ATTR_IFINDEX, skb->dev ? skb->dev->ifindex : skb->skb_iif))
goto nl_err;
if (nla_put(report_skb, IPOE_ATTR_ETH_HDR, sizeof(*eth), eth))
goto nl_err;
......@@ -866,14 +874,8 @@ static int ipoe_create(__be32 peer_addr, __be32 addr, const char *link_ifname, c
struct ipoe_session *ses;
struct net_device *dev, *link_dev = NULL;
char name[IFNAMSIZ];
int r = 0;
int h;
#ifdef __LITTLE_ENDIAN
h = ((peer_addr >> 24) ^ (peer_addr >> 16)) & HASH_BITS;
#else
h = (peer_addr ^ (peer_addr >> 8)) & HASH_BITS;
#endif
int r = -EINVAL;
int h = hash_addr(peer_addr);
if (link_ifname) {
link_dev = dev_get_by_name(&init_net, link_ifname);
......@@ -884,8 +886,10 @@ static int ipoe_create(__be32 peer_addr, __be32 addr, const char *link_ifname, c
sprintf(name, "ipoe%%d");
dev = alloc_netdev(sizeof(*ses), name, ipoe_netdev_setup);
if (dev == NULL)
if (dev == NULL) {
r = -ENOMEM;
goto failed;
}
dev_net_set(dev, &init_net);
......@@ -929,7 +933,10 @@ static int ipoe_create(__be32 peer_addr, __be32 addr, const char *link_ifname, c
goto failed_free;
down(&ipoe_wlock);
list_add_tail_rcu(&ses->entry, &ipoe_list[h]);
if (peer_addr)
list_add_tail_rcu(&ses->entry, &ipoe_list[h]);
list_add_tail(&ses->entry2, &ipoe_list2);
r = dev->ifindex;
up(&ipoe_wlock);
return r;
......@@ -942,35 +949,6 @@ failed:
return r;
}
static int ipoe_delete(__be32 addr)
{
struct ipoe_session *ses;
down(&ipoe_wlock);
ses = ipoe_lookup(addr);
if (!ses) {
up(&ipoe_wlock);
return -EINVAL;
}
list_del_rcu(&ses->entry);
up(&ipoe_wlock);
atomic_dec(&ses->refs);
while (atomic_read(&ses->refs))
schedule_timeout_uninterruptible(1);
if (ses->link_dev)
dev_put(ses->link_dev);
unregister_netdev(ses->dev);
return 0;
}
static int ipoe_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
{
struct sk_buff *msg;
......@@ -1007,29 +985,61 @@ out:
static int ipoe_nl_cmd_create(struct sk_buff *skb, struct genl_info *info)
{
__be32 peer_addr, addr = 0;
struct sk_buff *msg;
void *hdr;
__be32 peer_addr = 0, addr = 0;
int ret = 0;
char ifname[IFNAMSIZ];
__u8 hwaddr[ETH_ALEN];
//struct net *net = genl_info_net(info);
if (!info->attrs[IPOE_ATTR_PEER_ADDR] || !info->attrs[IPOE_ATTR_IFNAME]) {
ret = -EINVAL;
msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (!msg) {
ret = -ENOMEM;
goto out;
}
peer_addr = nla_get_be32(info->attrs[IPOE_ATTR_PEER_ADDR]);
hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
&ipoe_nl_family, 0, IPOE_CMD_CREATE);
if (IS_ERR(hdr)) {
ret = PTR_ERR(hdr);
goto err_out;
}
if (info->attrs[IPOE_ATTR_PEER_ADDR])
peer_addr = nla_get_be32(info->attrs[IPOE_ATTR_PEER_ADDR]);
if (info->attrs[IPOE_ATTR_ADDR])
addr = nla_get_be32(info->attrs[IPOE_ATTR_ADDR]);
nla_strlcpy(ifname, info->attrs[IPOE_ATTR_IFNAME], IFNAMSIZ - 1);
if (info->attrs[IPOE_ATTR_IFNAME])
nla_strlcpy(ifname, info->attrs[IPOE_ATTR_IFNAME], IFNAMSIZ - 1);
if (info->attrs[IPOE_ATTR_HWADDR])
nla_memcpy(hwaddr, info->attrs[IPOE_ATTR_HWADDR], ETH_ALEN);
else
memset(hwaddr, 0, sizeof(hwaddr));
pr_info("ipoe: create %08x %08x %s\n", peer_addr, addr, ifname);
pr_info("ipoe: create %08x %08x %s\n", peer_addr, addr, info->attrs[IPOE_ATTR_IFNAME] ? ifname : "-");
ret = ipoe_create(peer_addr, addr, ifname, hwaddr);
ret = ipoe_create(peer_addr, addr, info->attrs[IPOE_ATTR_IFNAME] ? ifname : NULL, hwaddr);
if (ret < 0) {
nlmsg_free(msg);
return ret;
}
nla_put_u32(msg, IPOE_ATTR_IFINDEX, ret);
genlmsg_end(msg, hdr);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
return genlmsg_unicast(msg, info->snd_pid);
#else
return genlmsg_unicast(genl_info_net(info), msg, info->snd_pid);
#endif
err_out:
nlmsg_free(msg);
out:
return ret;
......@@ -1037,18 +1047,131 @@ out:
static int ipoe_nl_cmd_delete(struct sk_buff *skb, struct genl_info *info)
{
__be32 addr;
//struct net *net = genl_info_net(info);
struct net_device *dev;
int ifindex;
int r = 0;
int ret = -EINVAL;
if (!info->attrs[IPOE_ATTR_PEER_ADDR])
if (!info->attrs[IPOE_ATTR_IFINDEX])
return -EINVAL;
addr = nla_get_u32(info->attrs[IPOE_ATTR_PEER_ADDR]);
ifindex = nla_get_u32(info->attrs[IPOE_ATTR_IFINDEX]);
down(&ipoe_wlock);
rcu_read_lock();
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
dev = dev_get_by_index_rcu(ifindex);
#else
dev = dev_get_by_index_rcu(&init_net, ifindex);
#endif
if (!dev || dev->header_ops != &ipoe_hard_header_ops)
r = 1;
rcu_read_unlock();
if (r)
goto out_unlock;
ses = netdev_priv(dev);
pr_info("ipoe: delete %08x\n", ses->peer_addr);
if (ses->peer_addr)
list_del_rcu(&ses->entry);
list_del(&ses->entry2);
up(&ipoe_wlock);
synchronize_rcu();
atomic_dec(&ses->refs);
while (atomic_read(&ses->refs))
schedule_timeout_uninterruptible(1);
if (ses->link_dev)
dev_put(ses->link_dev);
unregister_netdev(ses->dev);
ret = 0;
out_unlock:
up(&ipoe_wlock);
out:
return ret;
}
static int ipoe_nl_cmd_modify(struct sk_buff *skb, struct genl_info *info)
{
int ret = -EINVAL, r = 0;
char ifname[IFNAMSIZ];
struct net_device *dev, *link_dev, *old_dev;
struct ipoe_session *ses;
int ifindex;
if (!info->attrs[IPOE_ATTR_IFINDEX])
goto out;
down(&ipoe_wlock);
ifindex = nla_get_be32(info->attrs[IPOE_ATTR_IFINDEX]);
rcu_read_lock();
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35)
dev = dev_get_by_index_rcu(ifindex);
#else
dev = dev_get_by_index_rcu(&init_net, ifindex);
#endif
if (!dev || dev->header_ops != &ipoe_hard_header_ops)
r = 1;
rcu_read_unlock();
pr_info("ipoe: delete %08x\n", addr);
if (r)
goto out_unlock;
return ipoe_delete(addr);
ses = netdev_priv(dev);
if (info->attrs[IPOE_ATTR_IFNAME]) {
nla_strlcpy(ifname, info->attrs[IPOE_ATTR_IFNAME], IFNAMSIZ - 1);
link_dev = dev_get_by_name(&init_net, ifname);
if (!link_dev)
goto out_unlock;
old_dev = ses->link_dev;
ses->link_dev = link_dev;
if (old_dev)
dev_put(old_dev);
}
if (info->attrs[IPOE_ATTR_PEER_ADDR]) {
if (ses->peer_addr) {
list_del_rcu(&ses->entry);
synchronize_rcu();
}
ses->peer_addr = nla_get_be32(info->attrs[IPOE_ATTR_PEER_ADDR]);
if (ses->peer_addr)
list_add_tail_rcu(&ses->entry, &ipoe_list[hash_addr(ses->peer_addr)])
}
if (info->attrs[IPOE_ATTR_ADDR])
ses->addr = nla_get_be32(info->attrs[IPOE_ATTR_ADDR]);
if (info->attrs[IPOE_ATTR_HWADDR])
nla_memcpy(ses->hwaddr, info->attrs[IPOE_ATTR_HWADDR], ETH_ALEN);
pr_info("ipoe: modify %08x %08x\n", ses->peer_addr, ses->addr);
ret = 0;
out_unlock:
up(&ipoe_wlock);
out:
return ret;
}
static int ipoe_nl_cmd_add_net(struct sk_buff *skb, struct genl_info *info)
......@@ -1064,7 +1187,7 @@ static int ipoe_nl_cmd_add_net(struct sk_buff *skb, struct genl_info *info)
n->addr = nla_get_u32(info->attrs[IPOE_ATTR_ADDR]);
n->mask = nla_get_u32(info->attrs[IPOE_ATTR_MASK]);
pr_info("add net %08x/%08x\n", n->addr, n->mask);
//pr_info("add net %08x/%08x\n", n->addr, n->mask);
down(&ipoe_wlock);
list_add_tail_rcu(&n->entry, &ipoe_networks);
......@@ -1086,7 +1209,7 @@ static int ipoe_nl_cmd_del_net(struct sk_buff *skb, struct genl_info *info)
rcu_read_lock();
list_for_each_entry_rcu(n, &ipoe_networks, entry) {
if (!addr || addr == n->addr) {
pr_info("del net %08x/%08x\n", n->addr, n->mask);
//pr_info("del net %08x/%08x\n", n->addr, n->mask);
list_del_rcu(&n->entry);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,0,0)
kfree_rcu(n, rcu_head);
......@@ -1132,6 +1255,12 @@ static struct genl_ops ipoe_nl_ops[] = {
.policy = ipoe_nl_policy,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = IPOE_CMD_MODIFY,
.doit = ipoe_nl_cmd_modify,
.policy = ipoe_nl_policy,
.flags = GENL_ADMIN_PERM,
},
{
.cmd = IPOE_CMD_ADD_NET,
.doit = ipoe_nl_cmd_add_net,
......@@ -1248,6 +1377,8 @@ static void __exit ipoe_fini(void)
{
struct ipoe_network *n;
struct ipoe_entry_u *e;
struct ipoe_session *ses;
int i;
genl_unregister_mc_group(&ipoe_nl_family, &ipoe_nl_mcg);
genl_unregister_family(&ipoe_nl_family);
......@@ -1259,9 +1390,25 @@ static void __exit ipoe_fini(void)
skb_queue_purge(&ipoe_queue);
del_timer(&ipoe_timer_u);
down(&ipoe_wlock);
up(&ipoe_wlock);
for (i = 0; i < HASH_BITS; i++)
rcu_assign_pointer(ipoe_list[i].next, &ipoe_list[i]);
rcu_barrier();
while (!list_empty(&ipoe_list2)) {
ses = list_entry(ipoe_list2.next, typeof(*ses), entry);
list_del(&ses->entry2);
if (ses->link_dev)
dev_put(ses->link_dev);
unregister_netdev(ses->dev);
}
while (!list_empty(&ipoe_networks)) {
n = list_entry(ipoe_networks.next, typeof(*n), entry);
list_del(&n->entry);
......
#ifndef __LINUX_ISG_H
#define __LINUX_ISG_H
#ifndef __LINUX_IPOE_H
#define __LINUX_IPOE_H
#include <linux/types.h>
......@@ -7,6 +7,7 @@ enum {
IPOE_CMD_NOOP,
IPOE_CMD_CREATE,
IPOE_CMD_DELETE,
IPOE_CMD_MODIFY,
IPOE_CMD_ADD_NET,
IPOE_CMD_DEL_NET,
IPOE_REP_PKT,
......@@ -22,7 +23,7 @@ enum {
IPOE_ATTR_IFNAME, /* u32 */
IPOE_ATTR_HWADDR, /* u32 */
IPOE_ATTR_MASK, /* u32 */
IPOE_ATTR_PKT, /* u32 */
IPOE_ATTR_IFINDEX, /* u32 */
IPOE_ATTR_ETH_HDR, /* u32 */
IPOE_ATTR_IP_HDR, /* u32 */
__IPOE_ATTR_MAX,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册