diff options
-rw-r--r-- | accel-pppd/ctrl/ipoe/backup.c | 47 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/dhcpv4.c | 16 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/dhcpv4.h | 1 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/ipoe.c | 153 | ||||
-rw-r--r-- | accel-pppd/ctrl/ipoe/ipoe.h | 1 | ||||
-rw-r--r-- | accel-pppd/libnetlink/iplink.c | 72 | ||||
-rw-r--r-- | accel-pppd/libnetlink/iplink.h | 3 |
7 files changed, 288 insertions, 5 deletions
diff --git a/accel-pppd/ctrl/ipoe/backup.c b/accel-pppd/ctrl/ipoe/backup.c index 8347a4e..0bc6448 100644 --- a/accel-pppd/ctrl/ipoe/backup.c +++ b/accel-pppd/ctrl/ipoe/backup.c @@ -14,7 +14,7 @@ #include "ap_session_backup.h" #define IPOE_TAG_HWADDR 1 -#define IPOE_TAG_CLIENT_ID 2 +#define IPOE_TAG_CLIENT_ID 2 #define IPOE_TAG_AGENT_CIRCUIT_ID 3 #define IPOE_TAG_AGENT_REMOTE_ID 4 #define IPOE_TAG_XID 5 @@ -22,9 +22,16 @@ #define IPOE_TAG_CALLING_SID 7 #define IPOE_TAG_CALLED_SID 8 #define IPOE_TAG_IFNAME 9 +#define IPOE_TAG_FLAGS 10 +#define IPOE_TAG_YIADDR 11 +#define IPOE_TAG_SIADDR 12 +#define IPOE_TAG_MASK 13 #define IPOE_TAG_IFINDEX 100 +#define IPOE_FLAG_IFCFG 0x01 +#define IPOE_FLAG_DHCP_ADDR 0x02 +#define IPOE_FLAG_L4_REDIR 0x04 #define add_tag(id, data, size) if (!backup_add_tag(m, id, 0, data, size)) return -1; #define add_tag_i(id, data, size) if (!backup_add_tag(m, id, 1, data, size)) return -1; @@ -37,12 +44,26 @@ static void restore_complete(void); static int session_save(struct ap_session *ses, struct backup_mod *m) { struct ipoe_session *conn = container_of(ses, typeof(*conn), ses); + int flags = 0; + + if (conn->ifcfg) + flags |= IPOE_FLAG_IFCFG; + + if (conn->dhcp_addr) + flags |= IPOE_FLAG_DHCP_ADDR; + + if (conn->l4_redirect) + flags |= IPOE_FLAG_L4_REDIR; add_tag(IPOE_TAG_HWADDR, conn->hwaddr, 6); add_tag(IPOE_TAG_CALLING_SID, ses->ctrl->calling_station_id, strlen(ses->ctrl->calling_station_id)); add_tag(IPOE_TAG_CALLED_SID, ses->ctrl->called_station_id, strlen(ses->ctrl->called_station_id)); add_tag(IPOE_TAG_XID, &conn->xid, 4); add_tag(IPOE_TAG_GIADDR, &conn->giaddr, 4); + add_tag(IPOE_TAG_YIADDR, &conn->yiaddr, 4); + add_tag(IPOE_TAG_SIADDR, &conn->siaddr, 4); + add_tag(IPOE_TAG_MASK, &conn->mask, 1); + add_tag(IPOE_TAG_FLAGS, &flags, 4); if (conn->client_id) add_tag(IPOE_TAG_CLIENT_ID, conn->client_id->data, conn->client_id->len); @@ -83,6 +104,7 @@ static struct ap_session *ctrl_restore(struct backup_mod *m) int dlen = 0; uint8_t *ptr; struct ipoe_session_info *info; + int flags = 0; //if (!m->data->internal) // return NULL; @@ -149,9 +171,32 @@ static struct ap_session *ctrl_restore(struct backup_mod *m) case IPOE_TAG_IFINDEX: ses->ifindex = *(uint32_t *)t->data; break; + case IPOE_TAG_YIADDR: + ses->yiaddr = *(uint32_t *)t->data; + break; + case IPOE_TAG_SIADDR: + ses->siaddr = *(uint32_t *)t->data; + break; + case IPOE_TAG_MASK: + ses->mask = *(uint8_t *)t->data; + break; + case IPOE_TAG_FLAGS: + flags = *(uint32_t *)t->data; + break; } } + if (flags & IPOE_FLAG_IFCFG) + ses->ifcfg = 1; + + if (flags & IPOE_FLAG_DHCP_ADDR) { + dhcpv4_reserve_ip(ses->serv->dhcpv4, ses->yiaddr); + ses->dhcp_addr = 1; + } + + if (flags & IPOE_FLAG_L4_REDIR) + ses->l4_redirect = 1; + ses->serv = serv; triton_context_register(&ses->ctx, &ses->ses); diff --git a/accel-pppd/ctrl/ipoe/dhcpv4.c b/accel-pppd/ctrl/ipoe/dhcpv4.c index f1a8876..08da43a 100644 --- a/accel-pppd/ctrl/ipoe/dhcpv4.c +++ b/accel-pppd/ctrl/ipoe/dhcpv4.c @@ -721,6 +721,22 @@ int dhcpv4_get_ip(struct dhcpv4_serv *serv, uint32_t *yiaddr, uint32_t *siaddr, void dhcpv4_put_ip(struct dhcpv4_serv *serv, uint32_t ip) { int n = ntohl(ip) - serv->range->startip; + + if (n <= 0 || n / (8 * sizeof(long)) >= serv->range->len) + return; + + pthread_mutex_lock(&serv->range->lock); + serv->range->free[n / (8 * sizeof(long))] |= 1 << (n % (8 * sizeof(long))); + pthread_mutex_unlock(&serv->range->lock); +} + +void dhcpv4_reserve_ip(struct dhcpv4_serv *serv, uint32_t ip) +{ + int n = ntohl(ip) - serv->range->startip; + + if (n <= 0 || n / (8 * sizeof(long)) >= serv->range->len) + return; + pthread_mutex_lock(&serv->range->lock); serv->range->free[n / (8 * sizeof(long))] |= 1 << (n % (8 * sizeof(long))); pthread_mutex_unlock(&serv->range->lock); diff --git a/accel-pppd/ctrl/ipoe/dhcpv4.h b/accel-pppd/ctrl/ipoe/dhcpv4.h index cf3aac7..3ebb74c 100644 --- a/accel-pppd/ctrl/ipoe/dhcpv4.h +++ b/accel-pppd/ctrl/ipoe/dhcpv4.h @@ -102,5 +102,6 @@ void dhcpv4_print_packet(struct dhcpv4_packet *pack, void (*print)(const char *f int dhcpv4_get_ip(struct dhcpv4_serv *serv, uint32_t *yiaddr, uint32_t *siaddr, int *mask); void dhcpv4_put_ip(struct dhcpv4_serv *serv, uint32_t ip); +void dhcpv4_reserve_ip(struct dhcpv4_serv *serv, uint32_t ip); #endif diff --git a/accel-pppd/ctrl/ipoe/ipoe.c b/accel-pppd/ctrl/ipoe/ipoe.c index 69ffe36..711dca3 100644 --- a/accel-pppd/ctrl/ipoe/ipoe.c +++ b/accel-pppd/ctrl/ipoe/ipoe.c @@ -30,6 +30,9 @@ #include "iplink.h" #include "connlimit.h" +#ifdef RADIUS +#include "radius.h" +#endif #include "ipoe.h" @@ -49,6 +52,13 @@ static int conf_ifcfg = 1; //static int conf_dhcpv6; static int conf_username; static int conf_unit_cache; +#ifdef RADIUS +static int conf_attr_dhcp_client_ip; +static int conf_attr_dhcp_router_ip; +static int conf_attr_dhcp_mask; +static int conf_attr_l4_redirect; +#endif +static int conf_l4_redirect_table; #ifdef USE_LUA static const char *conf_lua_username_func; @@ -182,6 +192,29 @@ static void ipoe_session_set_username(struct ipoe_session *ses) ses->ses.username = _strdup(ses->ses.ifname); } +static void ipoe_change_l4_redirect(struct ipoe_session *ses, int del) +{ + in_addr_t addr; + + if (conf_l4_redirect_table <= 0) + return; + + if (ses->ses.ipv4) + addr = ses->ses.ipv4->addr; + else + addr = ses->yiaddr; + + if (del) + iprule_del(addr, conf_l4_redirect_table); + else + iprule_add(addr, conf_l4_redirect_table); +} + +static void ipoe_change_addr(struct ipoe_session *ses, in_addr_t newaddr) +{ + +} + static void ipoe_session_start(struct ipoe_session *ses) { int r; @@ -256,9 +289,11 @@ static void ipoe_session_start(struct ipoe_session *ses) return; } - dhcpv4_get_ip(ses->serv->dhcpv4, &ses->yiaddr, &ses->siaddr, &ses->mask); - if (ses->yiaddr) - ses->dhcp_addr = 1; + if (!ses->yiaddr) { + dhcpv4_get_ip(ses->serv->dhcpv4, &ses->yiaddr, &ses->siaddr, &ses->mask); + if (ses->yiaddr) + ses->dhcp_addr = 1; + } ses->ses.ipv4 = ipdb_get_ipv4(&ses->ses); /*if (!ses->ses.ipv4) { @@ -390,6 +425,9 @@ static void ipoe_session_activate(struct ipoe_session *ses) if (ses->serv->opt_ifcfg) ipoe_ifcfg_add(ses); + + if (ses->l4_redirect) + ipoe_change_l4_redirect(ses, 0); ap_session_activate(&ses->ses); @@ -494,6 +532,11 @@ static void ipoe_session_finished(struct ap_session *s) static void ipoe_session_terminate(struct ap_session *s, int hard) { + struct ipoe_session *ses = container_of(s, typeof(*ses), ses); + + if (ses->l4_redirect) + ipoe_change_l4_redirect(ses, 1); + ap_session_finished(s); } @@ -814,6 +857,62 @@ void ipoe_recv_up(int ifindex, struct ethhdr *eth, struct iphdr *iph) } } +#ifdef RADIUS +static void ev_radius_access_accept(struct ev_radius_t *ev) +{ + struct ipoe_session *ses = container_of(ev->ses, typeof(*ses), ses); + struct rad_attr_t *attr; + + if (ev->ses->ctrl->type != CTRL_TYPE_IPOE) + return; + + list_for_each_entry(attr, &ev->reply->attrs, entry) { + if (attr->attr->id == conf_attr_dhcp_client_ip) + ses->yiaddr = attr->val.ipaddr; + else if (attr->attr->id == conf_attr_dhcp_router_ip) + ses->siaddr = attr->val.ipaddr; + else if (attr->attr->id == conf_attr_dhcp_mask) { + if (attr->val.integer > 0 && attr->val.integer < 31) + ses->mask = attr->val.integer; + } else if (attr->attr->id == conf_attr_l4_redirect) { + if (attr->attr->type == ATTR_TYPE_STRING) { + if (attr->len && attr->val.string[0] != '0') + ses->l4_redirect = 1; + } else if (attr->val.integer != 0) + ses->l4_redirect = 1; + } + } +} + +static void ev_radius_coa(struct ev_radius_t *ev) +{ + struct ipoe_session *ses = container_of(ev->ses, typeof(*ses), ses); + struct rad_attr_t *attr; + int l4_redirect; + + if (ev->ses->ctrl->type != CTRL_TYPE_IPOE) + return; + + l4_redirect = ses->l4_redirect; + + list_for_each_entry(attr, &ev->request->attrs, entry) { + if (attr->attr->id == conf_attr_l4_redirect) { + if (attr->attr->type == ATTR_TYPE_STRING) + ses->l4_redirect = attr->len && attr->val.string[0] != '0'; + else + ses->l4_redirect = ((unsigned int)attr->val.integer) > 0; + } else if (strcmp(attr->attr->name, "Framed-IP-Address") == 0) { + if (ses->ses.ipv4 && ses->ses.ipv4->peer_addr != attr->val.ipaddr) + ipoe_change_addr(ses, attr->val.ipaddr); + } + } + + //if (l4_redirect && !ses->l4_redirect) || (!l4_redirect && ses->l4_redirect)) + if (l4_redirect != ses->l4_redirect) + ipoe_change_l4_redirect(ses, l4_redirect); +} +#endif + static void ipoe_serv_close(struct triton_context_t *ctx) { struct ipoe_serv *serv = container_of(ctx, typeof(*serv), ctx); @@ -1155,6 +1254,35 @@ static void load_local_nets(struct conf_sect_t *sect) } } +#ifdef RADIUS +static void parse_conf_rad_attr(const char *opt, int *val) +{ + struct rad_dict_attr_t *attr; + + opt = conf_get_opt("ipoe", opt); + + if (opt) { + if (atoi(opt) > 0) + *val = atoi(opt); + else { + attr = rad_dict_find_attr(opt); + if (attr) + *val = attr->id; + else + log_emerg("ipoe: couldn't find '%s' in dictionary\n", opt); + } + } else + *val = -1; +} +static void load_radius_attrs(void) +{ + parse_conf_rad_attr("attr-dhcp-client-ip", &conf_attr_dhcp_client_ip); + parse_conf_rad_attr("attr-dhcp-router-ip", &conf_attr_dhcp_router_ip); + parse_conf_rad_attr("attr-dhcp-mask", &conf_attr_dhcp_mask); + parse_conf_rad_attr("attr-l4-redirect", &conf_attr_l4_redirect); +} +#endif + static void load_config(void) { const char *opt; @@ -1210,6 +1338,12 @@ static void load_config(void) if (opt) conf_unit_cache = atoi(opt); + opt = conf_get_opt("ipoe", "l4-redirect-table"); + if (opt) + conf_l4_redirect_table = atoi(opt); + else + conf_l4_redirect_table = 0; + opt = conf_get_opt("ipoe", "shared"); if (opt) conf_shared = atoi(opt); @@ -1248,6 +1382,11 @@ static void load_config(void) if (!conf_dhcpv4 && !conf_up) conf_dhcpv4 = 1; +#ifdef RADIUS + if (triton_module_loaded("radius")) + load_radius_attrs(); +#endif + load_interfaces(s); load_local_nets(s); } @@ -1262,6 +1401,12 @@ static void ipoe_init(void) cli_register_simple_cmd2(show_stat_exec, NULL, 2, "show", "stat"); triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config); + +#ifdef RADIUS + if (triton_module_loaded("radius")) + triton_event_register_handler(EV_RADIUS_ACCESS_ACCEPT, (triton_event_func)ev_radius_access_accept); + triton_event_register_handler(EV_RADIUS_COA, (triton_event_func)ev_radius_coa); +#endif } -DEFINE_INIT(20, ipoe_init); +DEFINE_INIT(52, ipoe_init); diff --git a/accel-pppd/ctrl/ipoe/ipoe.h b/accel-pppd/ctrl/ipoe/ipoe.h index edf9754..b955b95 100644 --- a/accel-pppd/ctrl/ipoe/ipoe.h +++ b/accel-pppd/ctrl/ipoe/ipoe.h @@ -55,6 +55,7 @@ struct ipoe_session int ifindex; int ifcfg:1; int dhcp_addr:1; + int l4_redirect:1; }; struct ipoe_session_info diff --git a/accel-pppd/libnetlink/iplink.c b/accel-pppd/libnetlink/iplink.c index 779a8ae..4b85afe 100644 --- a/accel-pppd/libnetlink/iplink.c +++ b/accel-pppd/libnetlink/iplink.c @@ -14,6 +14,7 @@ //#include <linux/if_link.h> //#include <linux/if_addr.h> //#include <linux/rtnetlink.h> +#include <linux/fib_rules.h> #include "triton.h" #include "log.h" @@ -294,6 +295,77 @@ int __export iproute_del(int ifindex, in_addr_t dst) return 0; } +int __export iprule_add(uint32_t addr, int table) +{ + struct ipaddr_req { + struct nlmsghdr n; + struct rtmsg i; + char buf[1024]; + } req; + + if (!rth) + open_rth(); + + if (!rth) + return -1; + + memset(&req, 0, sizeof(req) - 1024); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_NEWRULE; + req.i.rtm_family = AF_INET; + req.i.rtm_table = table < 256 ? table : RT_TABLE_UNSPEC; + req.i.rtm_scope = RT_SCOPE_UNIVERSE; + req.i.rtm_protocol = RTPROT_BOOT; + req.i.rtm_type = RTN_UNICAST; + req.i.rtm_src_len = 32; + + addattr32(&req.n, sizeof(req), FRA_SRC, addr); + if (table >= 256) + addattr32(&req.n, sizeof(req), FRA_TABLE, table); + + if (rtnl_talk(rth, &req.n, 0, 0, NULL, NULL, NULL, 0) < 0) + return -1; + + return 0; +} + +int __export iprule_del(uint32_t addr, int table) +{ + struct ipaddr_req { + struct nlmsghdr n; + struct rtmsg i; + char buf[1024]; + } req; + + if (!rth) + open_rth(); + + if (!rth) + return -1; + + memset(&req, 0, sizeof(req) - 1024); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_DELRULE; + req.i.rtm_family = AF_INET; + req.i.rtm_table = table < 256 ? table : RT_TABLE_UNSPEC; + req.i.rtm_scope = RT_SCOPE_UNIVERSE; + req.i.rtm_protocol = RTPROT_BOOT; + req.i.rtm_type = RTN_UNICAST; + req.i.rtm_src_len = 32; + + addattr32(&req.n, sizeof(req), FRA_SRC, addr); + if (table >= 256) + addattr32(&req.n, sizeof(req), FRA_TABLE, table); + + if (rtnl_talk(rth, &req.n, 0, 0, NULL, NULL, NULL, 0) < 0) + return -1; + + return 0; +} static void init(void) diff --git a/accel-pppd/libnetlink/iplink.h b/accel-pppd/libnetlink/iplink.h index a6af662..f912434 100644 --- a/accel-pppd/libnetlink/iplink.h +++ b/accel-pppd/libnetlink/iplink.h @@ -13,4 +13,7 @@ int ipaddr_del(int ifindex, in_addr_t addr); int iproute_add(int ifindex, in_addr_t src, in_addr_t dst); int iproute_del(int ifindex, in_addr_t dst); + +int iprule_add(uint32_t addr, int table); +int iprule_del(uint32_t addr, int table); #endif |