summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKozlov Dmitry <xeb@mail.ru>2012-07-19 19:09:24 +0400
committerKozlov Dmitry <xeb@mail.ru>2012-07-19 19:09:24 +0400
commita60d08c8ca66de1844a430cc487a725e4c0e0d54 (patch)
tree2c05a0acafefbfd22c2d8cc4093ae7ba6946b8c1
parent09b155588131d8ed6f380aec467d0fa7749933b8 (diff)
downloadaccel-ppp-a60d08c8ca66de1844a430cc487a725e4c0e0d54.tar.gz
accel-ppp-a60d08c8ca66de1844a430cc487a725e4c0e0d54.zip
ipoe: implemented L4-Redirect radius attribute
ipoe: implemented client address, router address and mask to be passed via radius
-rw-r--r--accel-pppd/ctrl/ipoe/backup.c47
-rw-r--r--accel-pppd/ctrl/ipoe/dhcpv4.c16
-rw-r--r--accel-pppd/ctrl/ipoe/dhcpv4.h1
-rw-r--r--accel-pppd/ctrl/ipoe/ipoe.c153
-rw-r--r--accel-pppd/ctrl/ipoe/ipoe.h1
-rw-r--r--accel-pppd/libnetlink/iplink.c72
-rw-r--r--accel-pppd/libnetlink/iplink.h3
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