summaryrefslogtreecommitdiff
path: root/accel-pppd
diff options
context:
space:
mode:
authorKozlov Dmitry <xeb@mail.ru>2012-08-03 14:31:52 +0400
committerKozlov Dmitry <xeb@mail.ru>2012-08-03 14:31:52 +0400
commit95586a13a28c93e60f8da6b728b0e7b57cdff5f1 (patch)
treeaf7e8c580068974b5b60d564b6fb45b7efef6ba0 /accel-pppd
parentff80a772e3116afa4513397db2aa51962ff32994 (diff)
downloadaccel-ppp-95586a13a28c93e60f8da6b728b0e7b57cdff5f1.tar.gz
accel-ppp-95586a13a28c93e60f8da6b728b0e7b57cdff5f1.zip
ipoe: implemented dhcp relay
Diffstat (limited to 'accel-pppd')
-rw-r--r--accel-pppd/accel-ppp.conf1
-rw-r--r--accel-pppd/accel-ppp.conf.517
-rw-r--r--accel-pppd/ctrl/ipoe/backup.c60
-rw-r--r--accel-pppd/ctrl/ipoe/dhcpv4.c422
-rw-r--r--accel-pppd/ctrl/ipoe/dhcpv4.h31
-rw-r--r--accel-pppd/ctrl/ipoe/ipoe.c440
-rw-r--r--accel-pppd/ctrl/ipoe/ipoe.h19
-rw-r--r--accel-pppd/ctrl/ipoe/lua.c4
8 files changed, 754 insertions, 240 deletions
diff --git a/accel-pppd/accel-ppp.conf b/accel-pppd/accel-ppp.conf
index 64c69e80..0199f058 100644
--- a/accel-pppd/accel-ppp.conf
+++ b/accel-pppd/accel-ppp.conf
@@ -45,6 +45,7 @@ shared=0
ifcfg=1
mode=L2
start=dhcpv4
+#relay=10.10.10.10
#attr-dhcp-client-ip=DHCP-Client-IP-Address
#attr-dhcp-router-ip=DHCP-Router-IP-Address
#attr-dhcp-mask=DHCP-Mask
diff --git a/accel-pppd/accel-ppp.conf.5 b/accel-pppd/accel-ppp.conf.5
index 1dc9e859..82dd84a8 100644
--- a/accel-pppd/accel-ppp.conf.5
+++ b/accel-pppd/accel-ppp.conf.5
@@ -229,8 +229,15 @@ Specifies default value for per-interface
.B ifcfg
parameter.
.TP
+.BI "relay=" ipv4_address
+Specifies default value for per-interface
+.B relay
+parameter.
+.TP
.BI "interface=" [re:]name[,mode=L2|L3][,shared=0|1][,start=dhcpv4|up]
.BI "" [,range=x.x.x.x/mask][,ifcfg=0|1]
+.BI "" [,relay=x.x.x.x]
+.BI "" [,giaddr=x.x.x.x]
.br
Specifies interface to listen dhcp or unclassified packets. You may specify multiple
.B interface
@@ -267,6 +274,16 @@ parameter specifies local range of ip address to give to dhcp clients. First IP
The
.B ifcfg
parameter specifies whether accel-ppp should add router IP address and route to client to interface or it is explicitly configured.
+.br
+The
+.B relay
+parameter specifies DHCPv4 relay IP address to pass requests to. If specified
+.B giaddr
+is also needed.
+.br
+The
+.B giaddr
+parameter specifies relay agent IP address.
.TP
.BI "local-net=" x.x.x.x/mask
Specifies networks from which packets will be treated as unclassified. You may specify multiple local-net options.
diff --git a/accel-pppd/ctrl/ipoe/backup.c b/accel-pppd/ctrl/ipoe/backup.c
index 0bc64484..9633bf71 100644
--- a/accel-pppd/ctrl/ipoe/backup.c
+++ b/accel-pppd/ctrl/ipoe/backup.c
@@ -15,8 +15,7 @@
#define IPOE_TAG_HWADDR 1
#define IPOE_TAG_CLIENT_ID 2
-#define IPOE_TAG_AGENT_CIRCUIT_ID 3
-#define IPOE_TAG_AGENT_REMOTE_ID 4
+#define IPOE_TAG_RELAY_AGENT 3
#define IPOE_TAG_XID 5
#define IPOE_TAG_GIADDR 6
#define IPOE_TAG_CALLING_SID 7
@@ -26,12 +25,16 @@
#define IPOE_TAG_YIADDR 11
#define IPOE_TAG_SIADDR 12
#define IPOE_TAG_MASK 13
+#define IPOE_TAG_RELAY_SERVER_ID 14
+#define IPOE_TAG_LEASE_TIME 15
#define IPOE_TAG_IFINDEX 100
-#define IPOE_FLAG_IFCFG 0x01
-#define IPOE_FLAG_DHCP_ADDR 0x02
-#define IPOE_FLAG_L4_REDIR 0x04
+#define IPOE_FLAG_IFCFG 0x01
+#define IPOE_FLAG_DHCP_ADDR 0x02
+#define IPOE_FLAG_RELAY_ADDR 0x04
+#define IPOE_FLAG_L4_REDIR 0x08
+#define IPOE_FLAG_L4_REDIR_SET 0x10
#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;
@@ -52,8 +55,14 @@ static int session_save(struct ap_session *ses, struct backup_mod *m)
if (conn->dhcp_addr)
flags |= IPOE_FLAG_DHCP_ADDR;
+ if (conn->relay_addr)
+ flags |= IPOE_FLAG_RELAY_ADDR;
+
if (conn->l4_redirect)
flags |= IPOE_FLAG_L4_REDIR;
+
+ if (conn->l4_redirect_set)
+ flags |= IPOE_FLAG_L4_REDIR_SET;
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));
@@ -64,16 +73,16 @@ static int session_save(struct ap_session *ses, struct backup_mod *m)
add_tag(IPOE_TAG_SIADDR, &conn->siaddr, 4);
add_tag(IPOE_TAG_MASK, &conn->mask, 1);
add_tag(IPOE_TAG_FLAGS, &flags, 4);
+ add_tag(IPOE_TAG_RELAY_SERVER_ID, &conn->relay_server_id, 4);
+ add_tag(IPOE_TAG_LEASE_TIME, &conn->lease_time, 4);
+ add_tag(IPOE_TAG_IFNAME, conn->serv->ifname, strlen(conn->serv->ifname) + 1);
if (conn->client_id)
add_tag(IPOE_TAG_CLIENT_ID, conn->client_id->data, conn->client_id->len);
- if (conn->agent_circuit_id)
- add_tag(IPOE_TAG_AGENT_CIRCUIT_ID, conn->agent_circuit_id->data, conn->agent_circuit_id->len);
- if (conn->agent_circuit_id)
- add_tag(IPOE_TAG_AGENT_REMOTE_ID, conn->agent_remote_id->data, conn->agent_remote_id->len);
-
- add_tag(IPOE_TAG_IFNAME, conn->serv->ifname, strlen(conn->serv->ifname) + 1);
+ if (conn->relay_agent)
+ add_tag(IPOE_TAG_RELAY_AGENT, conn->relay_agent->data, conn->relay_agent->len);
+
add_tag_i(IPOE_TAG_IFINDEX, &conn->ifindex, 4);
return 0;
@@ -87,9 +96,9 @@ static int session_restore(struct ap_session *ses, struct backup_mod *m)
return 0;
}
-static void set_dhcpv4_opt(struct dhcp_opt **opt, struct backup_tag *t, uint8_t **ptr)
+static void set_dhcpv4_opt(struct dhcpv4_option **opt, struct backup_tag *t, uint8_t **ptr)
{
- *opt = (struct dhcp_opt *)(*ptr);
+ *opt = (struct dhcpv4_option *)(*ptr);
(*opt)->len = t->size;
memcpy((*opt)->data, t->data, t->size);
(*ptr) += sizeof(**opt) + t->size;
@@ -112,9 +121,8 @@ static struct ap_session *ctrl_restore(struct backup_mod *m)
list_for_each_entry(t, &m->tag_list, entry) {
switch(t->id) {
case IPOE_TAG_CLIENT_ID:
- case IPOE_TAG_AGENT_CIRCUIT_ID:
- case IPOE_TAG_AGENT_REMOTE_ID:
- dlen += sizeof(struct dhcp_opt) + t->size;
+ case IPOE_TAG_RELAY_AGENT:
+ dlen += sizeof(struct dhcpv4_option) + t->size;
break;
case IPOE_TAG_IFNAME:
ifname = t;
@@ -162,11 +170,9 @@ static struct ap_session *ctrl_restore(struct backup_mod *m)
case IPOE_TAG_CLIENT_ID:
set_dhcpv4_opt(&ses->client_id, t, &ptr);
break;
- case IPOE_TAG_AGENT_CIRCUIT_ID:
- set_dhcpv4_opt(&ses->agent_circuit_id, t, &ptr);
- break;
- case IPOE_TAG_AGENT_REMOTE_ID:
- set_dhcpv4_opt(&ses->agent_remote_id, t, &ptr);
+ case IPOE_TAG_RELAY_AGENT:
+ set_dhcpv4_opt(&ses->relay_agent, t, &ptr);
+ dhcpv4_parse_opt82(ses->relay_agent, &ses->agent_circuit_id, &ses->agent_remote_id);
break;
case IPOE_TAG_IFINDEX:
ses->ifindex = *(uint32_t *)t->data;
@@ -183,6 +189,12 @@ static struct ap_session *ctrl_restore(struct backup_mod *m)
case IPOE_TAG_FLAGS:
flags = *(uint32_t *)t->data;
break;
+ case IPOE_TAG_RELAY_SERVER_ID:
+ ses->relay_server_id = *(uint32_t *)t->data;
+ break;
+ case IPOE_TAG_LEASE_TIME:
+ ses->lease_time = *(uint32_t *)t->data;
+ break;
}
}
@@ -194,8 +206,14 @@ static struct ap_session *ctrl_restore(struct backup_mod *m)
ses->dhcp_addr = 1;
}
+ if (flags & IPOE_FLAG_RELAY_ADDR)
+ ses->relay_addr = 1;
+
if (flags & IPOE_FLAG_L4_REDIR)
ses->l4_redirect = 1;
+
+ if (flags & IPOE_FLAG_L4_REDIR_SET && m->data->internal)
+ ses->l4_redirect = 1;
ses->serv = serv;
diff --git a/accel-pppd/ctrl/ipoe/dhcpv4.c b/accel-pppd/ctrl/ipoe/dhcpv4.c
index 08da43ac..86aba875 100644
--- a/accel-pppd/ctrl/ipoe/dhcpv4.c
+++ b/accel-pppd/ctrl/ipoe/dhcpv4.c
@@ -25,13 +25,14 @@
#include "dhcpv4.h"
-#define DHCP_SERV_PORT 67
-#define DHCP_CLIENT_PORT 68
-#define DHCP_MAGIC "\x63\x82\x53\x63"
-
-
#define BUF_SIZE 4096
+struct dhcpv4_relay_ctx
+{
+ struct list_head entry;
+ struct triton_context_t *ctx;
+ triton_event_func recv;
+};
static int conf_verbose;
static in_addr_t conf_dns1;
@@ -40,6 +41,8 @@ static in_addr_t conf_dns2;
static mempool_t pack_pool;
static mempool_t opt_pool;
+static LIST_HEAD(relay_list);
+
static int dhcpv4_read(struct triton_md_handler_t *h);
static struct dhcpv4_iprange *parse_range(const char *str)
@@ -228,11 +231,11 @@ void dhcpv4_free(struct dhcpv4_serv *serv)
_free(serv);
}
-void dhcpv4_print_packet(struct dhcpv4_packet *pack, void (*print)(const char *fmt, ...))
+void dhcpv4_print_packet(struct dhcpv4_packet *pack, int relay, void (*print)(const char *fmt, ...))
{
const char *msg_name[] = {"Discover", "Offer", "Request", "Decline", "Ack", "Nak", "Release", "Inform"};
- print("[DHCPv4 %s xid=%x ", msg_name[pack->msg_type - 1], pack->hdr->xid);
+ print("[DHCPv4 %s%s xid=%x ", relay ? "relay " : "", msg_name[pack->msg_type - 1], pack->hdr->xid);
if (pack->hdr->ciaddr)
print("ciaddr=%i.%i.%i.%i ",
@@ -276,41 +279,6 @@ void dhcpv4_print_packet(struct dhcpv4_packet *pack, void (*print)(const char *f
print("]\n");
}
-static int parse_opt82(struct dhcpv4_packet *pack, struct dhcpv4_option *opt)
-{
- uint8_t *ptr = opt->data;
- uint8_t *endptr = ptr + opt->len;
- int type, len;
- struct dhcpv4_option *opt1;
-
- while (ptr < endptr) {
- type = *ptr++;
- len = *ptr++;
- if (ptr + len > endptr)
- return -1;
- if (type == 1 || type == 2) {
- opt1 = mempool_alloc(opt_pool);
- if (!opt1) {
- log_emerg("out of memory\n");
- return -1;
- }
-
- opt1->type = type;
- opt1->len = len;
- opt1->data = ptr;
-
- if (type == 1)
- pack->agent_circuit_id = opt1;
- else
- pack->agent_remote_id = opt1;
- }
-
- ptr += len;
- }
-
- return 0;
-}
-
static int dhcpv4_parse_packet(struct dhcpv4_packet *pack, int len)
{
struct dhcpv4_option *opt;
@@ -321,9 +289,6 @@ static int dhcpv4_parse_packet(struct dhcpv4_packet *pack, int len)
log_warn("dhcpv4: short packet received\n");
return -1;
}
-
- if (pack->hdr->op != DHCP_OP_REQUEST)
- return -1;
if (pack->hdr->htype != 1)
return -1;
@@ -342,8 +307,10 @@ static int dhcpv4_parse_packet(struct dhcpv4_packet *pack, int len)
continue;
}
- if (*ptr == 0xff)
+ if (*ptr == 0xff) {
+ ptr++;
break;
+ }
opt = mempool_alloc(opt_pool);
if (!opt) {
@@ -364,7 +331,9 @@ static int dhcpv4_parse_packet(struct dhcpv4_packet *pack, int len)
if (opt->type == 53)
pack->msg_type = opt->data[0];
else if (opt->type == 82)
- parse_opt82(pack, opt);
+ pack->relay_agent = opt;
+ else if (opt->type == 62)
+ pack->client_id = opt;
else if (opt->type == 50)
pack->request_ip = *(uint32_t *)opt->data;
else if (opt->type == 54)
@@ -377,6 +346,8 @@ static int dhcpv4_parse_packet(struct dhcpv4_packet *pack, int len)
if (dhcpv4_check_options(pack))
return -1;
+ pack->ptr = ptr;
+
/*if (conf_verbose) {
log_info2("recv ");
print_packet(pack, log_info2);
@@ -398,12 +369,71 @@ static struct dhcpv4_packet *dhcpv4_packet_alloc()
pack->hdr = (struct dhcpv4_hdr *)pack->data;
pack->ptr = (uint8_t *)(pack->hdr + 1);
+ pack->refs = 1;
memcpy(pack->hdr->magic, DHCP_MAGIC, 4);
return pack;
}
+void dhcpv4_packet_ref(struct dhcpv4_packet *pack)
+{
+ __sync_add_and_fetch(&pack->refs, 1);
+}
+
+struct dhcpv4_option *dhcpv4_packet_find_opt(struct dhcpv4_packet *pack, int type)
+{
+ struct dhcpv4_option *opt;
+
+ list_for_each_entry(opt, &pack->options, entry) {
+ if (opt->type == type)
+ return opt;
+ }
+
+ return NULL;
+}
+
+void dhcpv4_packet_free(struct dhcpv4_packet *pack)
+{
+ struct dhcpv4_option *opt;
+
+ if (__sync_sub_and_fetch(&pack->refs, 1))
+ return;
+
+ while (!list_empty(&pack->options)) {
+ opt = list_entry(pack->options.next, typeof(*opt), entry);
+ list_del(&opt->entry);
+ mempool_free(opt);
+ }
+
+ mempool_free(pack);
+}
+
+int dhcpv4_parse_opt82(struct dhcpv4_option *opt, uint8_t **agent_circuit_id, uint8_t **agent_remote_id)
+{
+ uint8_t *ptr = opt->data;
+ uint8_t *endptr = ptr + opt->len;
+ int type, len;
+
+ while (ptr < endptr) {
+ type = *ptr++;
+ len = *ptr++;
+
+ if (ptr + len > endptr)
+ return -1;
+
+ if (type == 1)
+ *agent_circuit_id = ptr - 1;
+ else if (type == 2)
+ *agent_remote_id = ptr - 1;
+
+ ptr += len;
+ }
+
+ return 0;
+}
+
+
static int dhcpv4_read(struct triton_md_handler_t *h)
{
struct dhcpv4_packet *pack;
@@ -433,12 +463,62 @@ static int dhcpv4_read(struct triton_md_handler_t *h)
dhcpv4_packet_free(pack);
continue;
}
+
+ if (pack->hdr->op != DHCP_OP_REQUEST) {
+ dhcpv4_packet_free(pack);
+ continue;
+ }
if (serv->recv)
serv->recv(serv, pack);
+
+ dhcpv4_packet_free(pack);
+ }
+}
+
+static int dhcpv4_relay_read(struct triton_md_handler_t *h)
+{
+ struct dhcpv4_packet *pack;
+ struct dhcpv4_relay *r = container_of(h, typeof(*r), hnd);
+ int n;
+ struct dhcpv4_relay_ctx *c;
+
+ while (1) {
+ pack = dhcpv4_packet_alloc();
+ if (!pack) {
+ log_emerg("out of memory\n");
+ return 1;
+ }
+
+ n = read(h->fd, pack->data, BUF_SIZE);
+ if (n == -1) {
+ mempool_free(pack);
+ if (errno == EAGAIN)
+ return 0;
+ log_error("dhcpv4: recv: %s\n", strerror(errno));
+ continue;
+ }
+
+ if (dhcpv4_parse_packet(pack, n)) {
+ dhcpv4_packet_free(pack);
+ continue;
+ }
+
+ if (pack->hdr->op != DHCP_OP_REPLY) {
+ dhcpv4_packet_free(pack);
+ continue;
+ }
+
+ list_for_each_entry(c, &r->ctx_list, entry) {
+ dhcpv4_packet_ref(pack);
+ triton_context_call(c->ctx, c->recv, pack);
+ }
+
+ dhcpv4_packet_free(pack);
}
}
+
uint16_t ip_csum(uint16_t *buf, int len)
{
uint32_t sum=0;
@@ -528,25 +608,6 @@ static int dhcpv4_send(struct dhcpv4_serv *serv, struct dhcpv4_packet *pack, in_
return dhcpv4_send_raw(serv, pack, saddr, daddr);
}
-void dhcpv4_packet_free(struct dhcpv4_packet *pack)
-{
- struct dhcpv4_option *opt;
-
- while (!list_empty(&pack->options)) {
- opt = list_entry(pack->options.next, typeof(*opt), entry);
- list_del(&opt->entry);
- mempool_free(opt);
- }
-
- if (pack->agent_circuit_id)
- mempool_free(pack->agent_circuit_id);
-
- if (pack->agent_remote_id)
- mempool_free(pack->agent_remote_id);
-
- mempool_free(pack);
-}
-
int dhcpv4_packet_add_opt(struct dhcpv4_packet *pack, int type, const void *data, int len)
{
struct dhcpv4_option *opt = mempool_alloc(opt_pool);
@@ -571,7 +632,7 @@ int dhcpv4_packet_add_opt(struct dhcpv4_packet *pack, int type, const void *data
return 0;
}
-int dhcpv4_send_reply(int msg_type, struct dhcpv4_serv *serv, struct dhcpv4_packet *req, uint32_t yiaddr, uint32_t siaddr, uint32_t mask, int lease_time)
+int dhcpv4_send_reply(int msg_type, struct dhcpv4_serv *serv, struct dhcpv4_packet *req, uint32_t yiaddr, uint32_t siaddr, uint32_t mask, int lease_time, struct dhcpv4_packet *relay)
{
struct dhcpv4_packet *pack;
int val, r;
@@ -579,6 +640,8 @@ int dhcpv4_send_reply(int msg_type, struct dhcpv4_serv *serv, struct dhcpv4_pack
in_addr_t dns1;
in_addr_t dns2;
} dns;
+ int dns_avail = 0;
+ struct dhcpv4_option *opt;
pack = dhcpv4_packet_alloc();
if (!pack) {
@@ -589,7 +652,7 @@ int dhcpv4_send_reply(int msg_type, struct dhcpv4_serv *serv, struct dhcpv4_pack
memcpy(pack->hdr, req->hdr, sizeof(*req->hdr));
pack->hdr->op = DHCP_OP_REPLY;
- pack->hdr->ciaddr = 0;
+ //pack->hdr->ciaddr = 0;
pack->hdr->yiaddr = yiaddr;
if (msg_type == DHCPOFFER)
pack->hdr->siaddr = siaddr;
@@ -612,15 +675,28 @@ int dhcpv4_send_reply(int msg_type, struct dhcpv4_serv *serv, struct dhcpv4_pack
val = htonl(~((1 << (32 - mask)) - 1));
if (dhcpv4_packet_add_opt(pack, 1, &val, 4))
goto out_err;
+
+ if (relay) {
+ list_for_each_entry(opt, &relay->options, entry) {
+ if (opt->type == 53 || opt->type == 54 || opt->type == 51 || opt->type == 3 || opt->type == 1)
+ continue;
+ if (opt->type == 6)
+ dns_avail = 1;
+ if (dhcpv4_packet_add_opt(pack, opt->type, opt->data, opt->len))
+ goto out_err;
+ }
+ }
- if (conf_dns1 && conf_dns2) {
- dns.dns1 = conf_dns1;
- dns.dns2 = conf_dns2;
- if (dhcpv4_packet_add_opt(pack, 6, &dns, 8))
- goto out_err;
- } else if (conf_dns1) {
- if (dhcpv4_packet_add_opt(pack, 6, &conf_dns1, 4))
- goto out_err;
+ if (!dns_avail) {
+ if (conf_dns1 && conf_dns2) {
+ dns.dns1 = conf_dns1;
+ dns.dns2 = conf_dns2;
+ if (dhcpv4_packet_add_opt(pack, 6, &dns, 8))
+ goto out_err;
+ } else if (conf_dns1) {
+ if (dhcpv4_packet_add_opt(pack, 6, &conf_dns1, 4))
+ goto out_err;
+ }
}
*pack->ptr++ = 255;
@@ -628,7 +704,7 @@ int dhcpv4_send_reply(int msg_type, struct dhcpv4_serv *serv, struct dhcpv4_pack
if (conf_verbose) {
pack->msg_type = msg_type;
log_ppp_info2("send ");
- dhcpv4_print_packet(pack, log_ppp_info2);
+ dhcpv4_print_packet(pack, 0, log_ppp_info2);
}
r = dhcpv4_send(serv, pack, siaddr, yiaddr);
@@ -669,7 +745,7 @@ int dhcpv4_send_nak(struct dhcpv4_serv *serv, struct dhcpv4_packet *req)
if (conf_verbose) {
pack->msg_type = DHCPNAK;
log_info2("send ");
- dhcpv4_print_packet(pack, log_info2);
+ dhcpv4_print_packet(pack, 0, log_info2);
}
r = dhcpv4_send(serv, pack, 0, 0xffffffff);
@@ -685,6 +761,196 @@ out_err:
return 0;
}
+struct dhcpv4_relay *dhcpv4_relay_create(const char *_addr, const char *_giaddr, struct triton_context_t *ctx, triton_event_func recv)
+{
+ struct dhcpv4_relay *r;
+ in_addr_t addr = inet_addr(_addr);
+ in_addr_t giaddr = inet_addr(_giaddr);
+ struct sockaddr_in raddr;
+ struct sockaddr_in laddr;
+ int sock = -1;
+ int f = 1;
+ struct dhcpv4_relay_ctx *c;
+
+ list_for_each_entry(r, &relay_list, entry) {
+ if (r->addr == addr && r->giaddr == giaddr)
+ goto found;
+ }
+
+ r = _malloc(sizeof(*r));
+ memset(r, 0, sizeof(*r));
+ INIT_LIST_HEAD(&r->ctx_list);
+ r->addr = addr;
+ r->giaddr = giaddr;
+
+ memset(&raddr, 0, sizeof(raddr));
+ raddr.sin_family = AF_INET;
+ raddr.sin_addr.s_addr = addr;
+ raddr.sin_port = htons(DHCP_SERV_PORT);
+
+ memset(&laddr, 0, sizeof(laddr));
+ laddr.sin_family = AF_INET;
+ laddr.sin_addr.s_addr = giaddr;
+ laddr.sin_port = htons(DHCP_SERV_PORT);
+
+ sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (!sock) {
+ log_error("socket: %s\n", strerror(errno));
+ goto out_err;
+ }
+
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &f, sizeof(f)))
+ log_error("dhcpv4: setsockopt(SO_REUSEADDR): %s\n", strerror(errno));
+
+ if (bind(sock, &laddr, sizeof(laddr))) {
+ log_error("dhcpv4: relay: %s: bind: %s\n", _addr, strerror(errno));
+ goto out_err;
+ }
+
+ if (connect(sock, &raddr, sizeof(raddr))) {
+ log_error("dhcpv4: relay: %s: connect: %s\n", _addr, strerror(errno));
+ goto out_err;
+ }
+
+ fcntl(sock, F_SETFL, O_NONBLOCK);
+ fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC);
+
+ r->hnd.fd = sock;
+ r->hnd.read = dhcpv4_relay_read;
+
+ triton_context_register(&r->ctx, NULL);
+ triton_md_register_handler(ctx, &r->hnd);
+ triton_md_enable_handler(&r->hnd, MD_MODE_READ);
+ triton_context_wakeup(&r->ctx);
+
+ list_add_tail(&r->entry, &relay_list);
+
+found:
+ c = _malloc(sizeof(*c));
+ c->ctx = ctx;
+ c->recv = recv;
+ list_add_tail(&c->entry, &r->ctx_list);
+
+ return r;
+
+out_err:
+ if (sock != -1)
+ close(sock);
+ _free(r);
+ return NULL;
+}
+
+void dhcpv4_relay_free(struct dhcpv4_relay *r, struct triton_context_t *ctx)
+{
+ struct dhcpv4_relay_ctx *c;
+
+ list_for_each_entry(c, &r->ctx_list, entry) {
+ if (c->ctx == ctx) {
+ list_del(&c->entry);
+ _free(c);
+ break;
+ }
+ }
+
+ if (list_empty(&r->ctx_list)) {
+ list_del(&r->entry);
+
+ triton_md_unregister_handler(&r->hnd);
+ close(r->hnd.fd);
+ triton_context_unregister(&r->ctx);
+ _free(r);
+ }
+}
+
+int dhcpv4_relay_send(struct dhcpv4_relay *relay, struct dhcpv4_packet *request, uint32_t server_id)
+{
+ int n;
+ int len = request->ptr - request->data;
+ uint32_t giaddr = request->hdr->giaddr;
+ struct dhcpv4_option *opt = NULL;
+ uint32_t _server_id;
+
+ request->hdr->giaddr = relay->giaddr;
+
+ if (server_id) {
+ opt = dhcpv4_packet_find_opt(request, 54);
+ if (opt) {
+ _server_id = *(uint32_t *)opt->data;
+ *(uint32_t *)opt->data = server_id;
+ }
+ }
+
+ if (conf_verbose) {
+ log_ppp_info2("send ");
+ dhcpv4_print_packet(request, 1, log_ppp_info2);
+ }
+
+ n = write(relay->hnd.fd, request->data, len);
+
+ request->hdr->giaddr = giaddr;
+
+ if (opt)
+ *(uint32_t *)opt->data = _server_id;
+
+ if (n != len)
+ return -1;
+
+ return 0;
+}
+
+int dhcpv4_relay_send_release(struct dhcpv4_relay *relay, uint8_t *chaddr, uint32_t xid, uint32_t ciaddr,
+ struct dhcpv4_option *client_id, struct dhcpv4_option *relay_agent)
+{
+ struct dhcpv4_packet *pack;
+ int n, len;
+
+ pack = dhcpv4_packet_alloc();
+ if (!pack) {
+ log_emerg("out of memory\n");
+ return -1;
+ }
+
+ memset(pack->hdr, 0, sizeof(*pack->hdr));
+
+ pack->msg_type = DHCPRELEASE;
+ pack->hdr->op = DHCP_OP_REQUEST;
+ pack->hdr->htype = 1;
+ pack->hdr->hlen = 6;
+ pack->hdr->ciaddr = ciaddr;
+ pack->hdr->giaddr = relay->giaddr;
+ pack->hdr->xid = xid;
+ memcpy(pack->hdr->magic, DHCP_MAGIC, 4);
+ memcpy(pack->hdr->chaddr, chaddr, 6);
+
+ if (dhcpv4_packet_add_opt(pack, 53, &pack->msg_type, 1))
+ goto out_err;
+
+ if (client_id && dhcpv4_packet_add_opt(pack, 61, client_id->data, client_id->len))
+ goto out_err;
+
+ if (relay_agent && dhcpv4_packet_add_opt(pack, 82, relay_agent->data, relay_agent->len))
+ goto out_err;
+
+ *pack->ptr = 255; pack->ptr++;
+
+ len = pack->ptr - pack->data;
+
+ if (conf_verbose) {
+ log_ppp_info2("send ");
+ dhcpv4_print_packet(pack, 1, log_ppp_info2);
+ }
+
+ n = write(relay->hnd.fd, pack->data, len);
+
+ dhcpv4_packet_free(pack);
+
+ return n == len ? 0 : -1;
+
+out_err:
+ dhcpv4_packet_free(pack);
+ return -1;
+}
+
int dhcpv4_get_ip(struct dhcpv4_serv *serv, uint32_t *yiaddr, uint32_t *siaddr, int *mask)
{
int i, k;
diff --git a/accel-pppd/ctrl/ipoe/dhcpv4.h b/accel-pppd/ctrl/ipoe/dhcpv4.h
index 3ebb74cc..67f7e61a 100644
--- a/accel-pppd/ctrl/ipoe/dhcpv4.h
+++ b/accel-pppd/ctrl/ipoe/dhcpv4.h
@@ -9,6 +9,10 @@
#define __packed __attribute__((packed))
+#define DHCP_SERV_PORT 67
+#define DHCP_CLIENT_PORT 68
+#define DHCP_MAGIC "\x63\x82\x53\x63"
+
#define DHCP_OP_REQUEST 1
#define DHCP_OP_REPLY 2
@@ -53,11 +57,11 @@ struct dhcpv4_packet
struct dhcpv4_hdr *hdr;
struct list_head options;
struct dhcpv4_option *client_id;
- struct dhcpv4_option *agent_circuit_id;
- struct dhcpv4_option *agent_remote_id;
+ struct dhcpv4_option *relay_agent;
uint32_t request_ip;
uint32_t server_id;
int msg_type;
+ int volatile refs;
uint8_t *ptr;
uint8_t data[0];
};
@@ -84,21 +88,40 @@ struct dhcpv4_serv
struct dhcpv4_iprange *range;
};
+struct dhcpv4_relay
+{
+ struct list_head entry;
+ struct triton_context_t ctx;
+ struct triton_md_handler_t hnd;
+ struct list_head ctx_list;
+ in_addr_t addr;
+ in_addr_t giaddr;
+};
+
struct ap_session;
struct dhcpv4_serv *dhcpv4_create(struct triton_context_t *ctx, const char *ifname, const char *opt);
void dhcpv4_free(struct dhcpv4_serv *);
+struct dhcpv4_relay *dhcpv4_relay_create(const char *addr, const char *giaddr, struct triton_context_t *ctx, triton_event_func recv);
+void dhcpv4_relay_free(struct dhcpv4_relay *, struct triton_context_t *);
+int dhcpv4_relay_send(struct dhcpv4_relay *relay, struct dhcpv4_packet *request, uint32_t server_id);
+int dhcpv4_relay_send_release(struct dhcpv4_relay *relay, uint8_t *chaddr, uint32_t xid, uint32_t ciaddr,
+ struct dhcpv4_option *client_id, struct dhcpv4_option *relay_agent);
-int dhcpv4_send_reply(int msg_type, struct dhcpv4_serv *serv, struct dhcpv4_packet *req, uint32_t yiaddr, uint32_t siaddr, uint32_t mask, int lease_time);
+int dhcpv4_send_reply(int msg_type, struct dhcpv4_serv *serv, struct dhcpv4_packet *req, uint32_t yiaddr, uint32_t siaddr, uint32_t mask, int lease_time, struct dhcpv4_packet *relay_reply);
int dhcpv4_send_nak(struct dhcpv4_serv *serv, struct dhcpv4_packet *req);
+void dhcpv4_packet_ref(struct dhcpv4_packet *pack);
+struct dhcpv4_option *dhcpv4_packet_find_opt(struct dhcpv4_packet *pack, int type);
void dhcpv4_packet_free(struct dhcpv4_packet *pack);
int dhcpv4_check_options(struct dhcpv4_packet *);
void dhcpv4_print_options(struct dhcpv4_packet *, void (*)(const char *, ...));
-void dhcpv4_print_packet(struct dhcpv4_packet *pack, void (*print)(const char *fmt, ...));
+void dhcpv4_print_packet(struct dhcpv4_packet *pack, int relay, void (*print)(const char *fmt, ...));
+
+int dhcpv4_parse_opt82(struct dhcpv4_option *opt, uint8_t **agent_circuit_id, uint8_t **agent_remote_id);
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);
diff --git a/accel-pppd/ctrl/ipoe/ipoe.c b/accel-pppd/ctrl/ipoe/ipoe.c
index 711dca39..14e8d375 100644
--- a/accel-pppd/ctrl/ipoe/ipoe.c
+++ b/accel-pppd/ctrl/ipoe/ipoe.c
@@ -59,6 +59,7 @@ static int conf_attr_dhcp_mask;
static int conf_attr_l4_redirect;
#endif
static int conf_l4_redirect_table;
+static const char *conf_relay;
#ifdef USE_LUA
static const char *conf_lua_username_func;
@@ -109,46 +110,54 @@ static void ipoe_serv_close(struct triton_context_t *ctx);
static struct ipoe_session *ipoe_session_lookup(struct ipoe_serv *serv, struct dhcpv4_packet *pack)
{
struct ipoe_session *ses;
- struct ipoe_session *ses1 = NULL;
+
+ uint8_t *agent_circuit_id = NULL;
+ uint8_t *agent_remote_id = NULL;
- list_for_each_entry(ses, &serv->sessions, entry) {
- if (pack->hdr->giaddr != ses->giaddr)
- continue;
+ if (pack->relay_agent && dhcpv4_parse_opt82(pack->relay_agent, &agent_circuit_id, &agent_remote_id)) {
+ agent_circuit_id = NULL;
+ agent_remote_id = NULL;
+ }
- if (pack->agent_circuit_id && !ses->agent_circuit_id)
+ list_for_each_entry(ses, &serv->sessions, entry) {
+ if (agent_circuit_id && !ses->agent_circuit_id)
continue;
- if (pack->agent_remote_id && !ses->agent_remote_id)
+ if (agent_remote_id && !ses->agent_remote_id)
continue;
- if (!pack->agent_circuit_id && ses->agent_circuit_id)
+ if (!agent_circuit_id && ses->agent_circuit_id)
continue;
- if (!pack->agent_remote_id && ses->agent_remote_id)
+ if (!agent_remote_id && ses->agent_remote_id)
continue;
- if (pack->agent_circuit_id) {
- if (pack->agent_circuit_id->len != ses->agent_circuit_id->len)
+ if (agent_circuit_id) {
+ if (*agent_circuit_id != *ses->agent_circuit_id)
continue;
- if (memcmp(pack->agent_circuit_id->data, ses->agent_circuit_id->data, pack->agent_circuit_id->len))
+ if (memcmp(agent_circuit_id + 1, ses->agent_circuit_id + 1, *agent_circuit_id))
continue;
}
- if (pack->agent_remote_id) {
- if (pack->agent_remote_id->len != ses->agent_remote_id->len)
+ if (agent_remote_id) {
+ if (*agent_remote_id != *ses->agent_remote_id)
continue;
- if (memcmp(pack->agent_remote_id->data, ses->agent_remote_id->data, pack->agent_remote_id->len))
+ if (memcmp(agent_remote_id + 1, ses->agent_remote_id + 1, *agent_remote_id))
continue;
-
+
return ses;
}
+
+ if (memcmp(pack->hdr->chaddr, ses->hwaddr, 6))
+ continue;
+
+ return ses;
- if (pack->client_id && !ses->client_id)
+ /*if (pack->client_id && !ses->client_id)
continue;
if (!pack->client_id && ses->client_id)
continue;
-
if (pack->client_id) {
if (pack->client_id->len != ses->client_id->len)
@@ -157,18 +166,15 @@ static struct ipoe_session *ipoe_session_lookup(struct ipoe_serv *serv, struct d
continue;
}
- if (memcmp(pack->hdr->chaddr, ses->hwaddr, 6))
- continue;
-
ses1 = ses;
if (pack->hdr->xid != ses->xid)
continue;
- return ses;
+ return ses;*/
}
- return ses1;
+ return NULL;
}
static void ipoe_session_timeout(struct triton_timer_t *t)
@@ -177,7 +183,7 @@ static void ipoe_session_timeout(struct triton_timer_t *t)
triton_timer_del(t);
- log_ppp_info2("session timed out\n");
+ log_ppp_info2("ipoe: session timed out\n");
ap_session_terminate(&ses->ses, TERM_LOST_CARRIER, 0);
}
@@ -204,10 +210,13 @@ static void ipoe_change_l4_redirect(struct ipoe_session *ses, int del)
else
addr = ses->yiaddr;
- if (del)
+ if (del) {
iprule_del(addr, conf_l4_redirect_table);
- else
+ ses->l4_redirect_set = 0;
+ } else {
iprule_add(addr, conf_l4_redirect_table);
+ ses->l4_redirect_set = 1;
+ }
}
static void ipoe_change_addr(struct ipoe_session *ses, in_addr_t newaddr)
@@ -215,6 +224,7 @@ static void ipoe_change_addr(struct ipoe_session *ses, in_addr_t newaddr)
}
+static void __ipoe_session_start(struct ipoe_session *ses);
static void ipoe_session_start(struct ipoe_session *ses)
{
int r;
@@ -225,6 +235,7 @@ static void ipoe_session_start(struct ipoe_session *ses)
if (ses->serv->opt_shared == 0 && (!ses->ses.ipv4 || ses->ses.ipv4->peer_addr == ses->yiaddr)) {
strncpy(ses->ses.ifname, ses->serv->ifname, AP_IFNAME_LEN);
ses->ses.ifindex = ses->serv->ifindex;
+ ses->ctrl.dont_ifcfg = 1;
} else if (ses->ifindex == -1) {
pthread_mutex_lock(&uc_lock);
if (!list_empty(&uc_list)) {
@@ -289,6 +300,18 @@ static void ipoe_session_start(struct ipoe_session *ses)
return;
}
+ if (ses->dhcpv4_request && ses->serv->dhcpv4_relay) {
+ dhcpv4_relay_send(ses->serv->dhcpv4_relay, ses->dhcpv4_request, ses->relay_server_id);
+
+ ses->timer.expire = ipoe_session_timeout;
+ ses->timer.expire_tv.tv_sec = conf_offer_timeout;
+ triton_timer_add(&ses->ctx, &ses->timer, 0);
+ } else
+ __ipoe_session_start(ses);
+}
+
+static void __ipoe_session_start(struct ipoe_session *ses)
+{
if (!ses->yiaddr) {
dhcpv4_get_ip(ses->serv->dhcpv4, &ses->yiaddr, &ses->siaddr, &ses->mask);
if (ses->yiaddr)
@@ -320,9 +343,9 @@ static void ipoe_session_start(struct ipoe_session *ses)
if (!ses->mask)
ses->mask = 24;
-
+
if (ses->dhcpv4_request) {
- dhcpv4_send_reply(DHCPOFFER, ses->serv->dhcpv4, ses->dhcpv4_request, ses->yiaddr, ses->siaddr, ses->mask, conf_lease_time);
+ dhcpv4_send_reply(DHCPOFFER, ses->serv->dhcpv4, ses->dhcpv4_request, ses->yiaddr, ses->siaddr, ses->mask, ses->lease_time, ses->dhcpv4_relay_reply);
dhcpv4_packet_free(ses->dhcpv4_request);
ses->dhcpv4_request = NULL;
@@ -338,38 +361,66 @@ static void ipoe_session_start(struct ipoe_session *ses)
}
}
-static void ipoe_ifcfg_add(struct ipoe_session *ses)
+static void ipoe_serv_add_addr(struct ipoe_serv *serv, in_addr_t addr)
{
struct ifaddr *a;
- struct ipoe_serv *serv = ses->serv;
- int f = 0;
pthread_mutex_lock(&serv->lock);
- if (ses->serv->opt_shared) {
- list_for_each_entry(a, &serv->addr_list, entry) {
- if (a->addr == ses->siaddr) {
- f = 1;
- break;
+ list_for_each_entry(a, &serv->addr_list, entry) {
+ if (a->addr == addr) {
+ a->refs++;
+ pthread_mutex_unlock(&serv->lock);
+
+ return;
+ }
+ }
+
+ a = _malloc(sizeof(*a));
+ a->addr = addr;
+ a->refs = 1;
+ list_add_tail(&a->entry, &serv->addr_list);
+
+ if (ipaddr_add(serv->ifindex, a->addr, 32))
+ log_warn("ipoe: failed to add addess to interface '%s'\n", serv->ifname);
+
+ pthread_mutex_unlock(&serv->lock);
+}
+
+static void ipoe_serv_del_addr(struct ipoe_serv *serv, in_addr_t addr)
+{
+ struct ifaddr *a;
+
+ pthread_mutex_lock(&serv->lock);
+
+ list_for_each_entry(a, &serv->addr_list, entry) {
+ if (a->addr == addr) {
+ if (--a->refs == 0) {
+ if (ipaddr_del(serv->ifindex, a->addr))
+ log_warn("ipoe: failed to delete addess from interface '%s'\n", serv->ifname);
+ list_del(&a->entry);
+ _free(a);
}
+ break;
}
- if (!f) {
- a = _malloc(sizeof(*a));
- a->addr = ses->siaddr;
- a->refs = 1;
- list_add_tail(&a->entry, &serv->addr_list);
-
- if (ipaddr_add(serv->ifindex, a->addr, 32))
- log_ppp_warn("ipoe: failed to add addess to interface '%s'\n", serv->ifname);
- } else
- a->refs++;
- } else {
+ }
+
+ pthread_mutex_unlock(&serv->lock);
+}
+
+static void ipoe_ifcfg_add(struct ipoe_session *ses)
+{
+ struct ipoe_serv *serv = ses->serv;
+
+ if (ses->serv->opt_shared || ses->serv->dhcpv4_relay)
+ ipoe_serv_add_addr(ses->serv, ses->siaddr);
+ else {
+ pthread_mutex_lock(&serv->lock);
if (ipaddr_add(serv->ifindex, ses->siaddr, 32))
log_ppp_warn("ipoe: failed to add addess to interface '%s'\n", serv->ifname);
+ pthread_mutex_unlock(&serv->lock);
}
- pthread_mutex_unlock(&serv->lock);
-
if (iproute_add(serv->ifindex, ses->siaddr, ses->yiaddr))
log_ppp_warn("ipoe: failed to add route to interface '%s'\n", serv->ifname);
@@ -378,34 +429,22 @@ static void ipoe_ifcfg_add(struct ipoe_session *ses)
static void ipoe_ifcfg_del(struct ipoe_session *ses)
{
- struct ifaddr *a;
struct ipoe_serv *serv = ses->serv;
if (iproute_del(serv->ifindex, ses->yiaddr))
log_ppp_warn("ipoe: failed to delete route from interface '%s'\n", serv->ifname);
- pthread_mutex_lock(&serv->lock);
-
- if (ses->serv->opt_shared) {
- list_for_each_entry(a, &serv->addr_list, entry) {
- if (a->addr == ses->siaddr)
- break;
- }
- if (--a->refs == 0) {
- if (ipaddr_del(serv->ifindex, a->addr))
- log_ppp_warn("ipoe: failed to delete addess from interface '%s'\n", serv->ifname);
- list_del(&a->entry);
- _free(a);
- }
+ if (ses->serv->opt_shared || ses->serv->dhcpv4_relay) {
+ ipoe_serv_del_addr(ses->serv, ses->siaddr);
} else {
+ pthread_mutex_lock(&serv->lock);
if (ipaddr_del(serv->ifindex, ses->siaddr))
log_ppp_warn("ipoe: failed to add addess to interface '%s'\n", serv->ifname);
+ pthread_mutex_unlock(&serv->lock);
}
-
- pthread_mutex_unlock(&serv->lock);
}
-static void ipoe_session_activate(struct ipoe_session *ses)
+static void __ipoe_session_activate(struct ipoe_session *ses)
{
uint32_t addr;
@@ -420,8 +459,7 @@ static void ipoe_session_activate(struct ipoe_session *ses)
ap_session_terminate(&ses->ses, TERM_NAS_ERROR, 0);
return;
}
- } else
- ses->ctrl.dont_ifcfg = 1;
+ }
if (ses->serv->opt_ifcfg)
ipoe_ifcfg_add(ses);
@@ -433,7 +471,7 @@ static void ipoe_session_activate(struct ipoe_session *ses)
if (ses->dhcpv4_request) {
if (ses->ses.state == AP_STATE_ACTIVE)
- dhcpv4_send_reply(DHCPACK, ses->serv->dhcpv4, ses->dhcpv4_request, ses->yiaddr, ses->siaddr, ses->mask, conf_lease_time);
+ dhcpv4_send_reply(DHCPACK, ses->serv->dhcpv4, ses->dhcpv4_request, ses->yiaddr, ses->siaddr, ses->mask, ses->lease_time, ses->dhcpv4_relay_reply);
else
dhcpv4_send_nak(ses->serv->dhcpv4, ses->dhcpv4_request);
@@ -442,16 +480,36 @@ static void ipoe_session_activate(struct ipoe_session *ses)
}
}
-static void ipoe_session_keepalive(struct ipoe_session *ses)
+static void ipoe_session_activate(struct ipoe_session *ses)
+{
+ if (ses->serv->dhcpv4_relay)
+ dhcpv4_relay_send(ses->serv->dhcpv4_relay, ses->dhcpv4_request, ses->relay_server_id);
+ else
+ __ipoe_session_activate(ses);
+}
+
+static void ipoe_session_keepalive(struct dhcpv4_packet *pack)
{
+ struct ipoe_session *ses = container_of(triton_context_self(), typeof(*ses), ctx);
+
+ if (ses->dhcpv4_request)
+ dhcpv4_packet_free(ses->dhcpv4_request);
+
+ ses->dhcpv4_request = pack;
+
if (ses->timer.tpd)
triton_timer_mod(&ses->timer, 0);
ses->xid = ses->dhcpv4_request->hdr->xid;
+
+ if (ses->ses.state == AP_STATE_ACTIVE && ses->serv->dhcpv4_relay) {
+ dhcpv4_relay_send(ses->serv->dhcpv4_relay, ses->dhcpv4_request, ses->relay_server_id);
+ return;
+ }
- if (ses->ses.state == AP_STATE_ACTIVE)
- dhcpv4_send_reply(DHCPACK, ses->serv->dhcpv4, ses->dhcpv4_request, ses->yiaddr, ses->siaddr, ses->mask, conf_lease_time);
- else
+ if (ses->ses.state == AP_STATE_ACTIVE) {
+ dhcpv4_send_reply(DHCPACK, ses->serv->dhcpv4, ses->dhcpv4_request, ses->yiaddr, ses->siaddr, ses->mask, ses->lease_time, ses->dhcpv4_relay_reply);
+ } else
dhcpv4_send_nak(ses->serv->dhcpv4, ses->dhcpv4_request);
dhcpv4_packet_free(ses->dhcpv4_request);
@@ -520,6 +578,9 @@ static void ipoe_session_finished(struct ap_session *s)
if (ses->dhcp_addr)
dhcpv4_put_ip(ses->serv->dhcpv4, ses->yiaddr);
+
+ if (ses->relay_addr && ses->serv->dhcpv4_relay)
+ dhcpv4_relay_send_release(ses->serv->dhcpv4_relay, ses->hwaddr, ses->xid, ses->yiaddr, ses->client_id, ses->relay_agent);
if (ses->ifcfg)
ipoe_ifcfg_del(ses);
@@ -534,7 +595,7 @@ 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)
+ if (ses->l4_redirect_set)
ipoe_change_l4_redirect(ses, 1);
ap_session_finished(s);
@@ -556,7 +617,7 @@ static struct ipoe_session *ipoe_session_create_dhcpv4(struct ipoe_serv *serv, s
struct ipoe_session *ses;
int dlen = 0;
uint8_t *ptr;
-
+
ses = mempool_alloc(ses_pool);
if (!ses) {
log_emerg("out of memery\n");
@@ -574,15 +635,13 @@ static struct ipoe_session *ipoe_session_create_dhcpv4(struct ipoe_serv *serv, s
ses->xid = pack->hdr->xid;
memcpy(ses->hwaddr, pack->hdr->chaddr, 6);
ses->giaddr = pack->hdr->giaddr;
+ ses->lease_time = conf_lease_time;
- if (pack->agent_circuit_id)
- dlen += sizeof(struct dhcp_opt) + pack->agent_circuit_id->len;
-
- if (pack->agent_remote_id)
- dlen += sizeof(struct dhcp_opt) + pack->agent_remote_id->len;
-
if (pack->client_id)
- dlen += sizeof(struct dhcp_opt) + pack->client_id->len;
+ dlen += sizeof(struct dhcpv4_option) + pack->client_id->len;
+
+ if (pack->relay_agent)
+ dlen += sizeof(struct dhcpv4_option) + pack->relay_agent->len;
if (dlen) {
ses->data = _malloc(dlen);
@@ -594,25 +653,20 @@ static struct ipoe_session *ipoe_session_create_dhcpv4(struct ipoe_serv *serv, s
ptr = ses->data;
}
- if (pack->agent_circuit_id) {
- ses->agent_circuit_id = (struct dhcp_opt *)ptr;
- ses->agent_circuit_id->len = pack->agent_circuit_id->len;
- memcpy(ses->agent_circuit_id->data, pack->agent_circuit_id->data, pack->agent_circuit_id->len);
- ptr += sizeof(struct dhcp_opt) + pack->agent_circuit_id->len;
- }
-
- if (pack->agent_remote_id) {
- ses->agent_remote_id = (struct dhcp_opt *)ptr;
- ses->agent_remote_id->len = pack->agent_remote_id->len;
- memcpy(ses->agent_remote_id->data, pack->agent_remote_id->data, pack->agent_remote_id->len);
- ptr += sizeof(struct dhcp_opt) + pack->agent_remote_id->len;
- }
-
if (pack->client_id) {
- ses->client_id = (struct dhcp_opt *)ptr;
+ ses->client_id = (struct dhcpv4_option *)ptr;
ses->client_id->len = pack->client_id->len;
memcpy(ses->client_id->data, pack->client_id->data, pack->client_id->len);
- ptr += sizeof(struct dhcp_opt) + pack->client_id->len;
+ ptr += sizeof(struct dhcpv4_option) + pack->client_id->len;
+ }
+
+ if (pack->relay_agent) {
+ ses->relay_agent = (struct dhcpv4_option *)ptr;
+ ses->relay_agent->len = pack->relay_agent->len;
+ memcpy(ses->relay_agent->data, pack->relay_agent->data, pack->relay_agent->len);
+ ptr += sizeof(struct dhcpv4_option) + pack->relay_agent->len;
+ if (dhcpv4_parse_opt82(ses->relay_agent, &ses->agent_circuit_id, &ses->agent_remote_id))
+ ses->relay_agent = NULL;
}
ses->ctx.before_switch = log_switch;
@@ -661,24 +715,25 @@ static void ipoe_recv_dhcpv4(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *p
ses = ipoe_session_lookup(serv, pack);
if (!ses) {
ses = ipoe_session_create_dhcpv4(serv, pack);
+ if (ses) {
+ dhcpv4_packet_ref(pack);
- if (conf_verbose && ses) {
- log_switch(dhcpv4->ctx, &ses->ses);
- log_ppp_info2("recv ");
- dhcpv4_print_packet(pack, log_ppp_info2);
+ if (conf_verbose) {
+ log_switch(dhcpv4->ctx, &ses->ses);
+ log_ppp_info2("recv ");
+ dhcpv4_print_packet(pack, 0, log_ppp_info2);
+ }
}
} else {
log_switch(dhcpv4->ctx, &ses->ses);
if (conf_verbose) {
log_ppp_info2("recv ");
- dhcpv4_print_packet(pack, log_ppp_info2);
+ dhcpv4_print_packet(pack, 0, log_ppp_info2);
}
- if (ses->ses.ipv4 && ses->ses.state == AP_STATE_ACTIVE && pack->request_ip == ses->ses.ipv4->peer_addr)
- dhcpv4_send_reply(DHCPOFFER, dhcpv4, pack, ses->yiaddr, ses->siaddr, ses->mask, conf_lease_time);
-
- dhcpv4_packet_free(pack);
+ if (ses->ses.state == AP_STATE_ACTIVE && pack->request_ip == ses->yiaddr)
+ dhcpv4_send_reply(DHCPOFFER, dhcpv4, pack, ses->yiaddr, ses->siaddr, ses->mask, ses->lease_time, ses->dhcpv4_relay_reply);
}
} else if (pack->msg_type == DHCPREQUEST) {
ses = ipoe_session_lookup(serv, pack);
@@ -686,7 +741,7 @@ static void ipoe_recv_dhcpv4(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *p
if (!ses) {
if (conf_verbose) {
log_info2("recv ");
- dhcpv4_print_packet(pack, log_info2);
+ dhcpv4_print_packet(pack, 0, log_info2);
}
dhcpv4_send_nak(dhcpv4, pack);
@@ -696,52 +751,146 @@ static void ipoe_recv_dhcpv4(struct dhcpv4_serv *dhcpv4, struct dhcpv4_packet *p
if (conf_verbose) {
log_info2("recv ");
- dhcpv4_print_packet(pack, log_info2);
+ dhcpv4_print_packet(pack, 0, log_info2);
}
- if (pack->server_id == ses->siaddr && pack->request_ip && pack->request_ip != ses->yiaddr)
+ if (pack->server_id == ses->siaddr)
dhcpv4_send_nak(dhcpv4, pack);
+ else if (ses->serv->dhcpv4_relay)
+ dhcpv4_relay_send(ses->serv->dhcpv4_relay, pack, 0);
ap_session_terminate(&ses->ses, TERM_USER_REQUEST, 0);
} else {
if (conf_verbose) {
log_switch(dhcpv4->ctx, &ses->ses);
log_ppp_info2("recv ");
- dhcpv4_print_packet(pack, log_ppp_info2);
+ dhcpv4_print_packet(pack, 0, log_ppp_info2);
}
if (serv->opt_shared == 0)
ipoe_drop_sessions(serv, ses);
- if (ses->ses.state == AP_STATE_STARTING && !ses->dhcpv4_request) {
+ if (ses->ses.state == AP_STATE_STARTING && ses->yiaddr && !ses->dhcpv4_request) {
ses->dhcpv4_request = pack;
- pack = NULL;
+ dhcpv4_packet_ref(pack);
triton_context_call(&ses->ctx, (triton_event_func)ipoe_session_activate, ses);
- } else if (ses->ses.state == AP_STATE_ACTIVE && !ses->dhcpv4_request) {
- ses->dhcpv4_request = pack;
- pack = NULL;
- triton_context_call(&ses->ctx, (triton_event_func)ipoe_session_keepalive, ses);
+ } else if (ses->ses.state == AP_STATE_ACTIVE) {
+ dhcpv4_packet_ref(pack);
+ triton_context_call(&ses->ctx, (triton_event_func)ipoe_session_keepalive, pack);
}
}
}
- if (pack)
- dhcpv4_packet_free(pack);
} else if (pack->msg_type == DHCPDECLINE || pack->msg_type == DHCPRELEASE) {
ses = ipoe_session_lookup(serv, pack);
if (ses) {
if (conf_verbose) {
log_switch(dhcpv4->ctx, &ses->ses);
log_ppp_info2("recv ");
- dhcpv4_print_packet(pack, log_ppp_info2);
+ dhcpv4_print_packet(pack, 0, log_ppp_info2);
}
+
+ if (pack->msg_type == DHCPDECLINE && ses->serv->dhcpv4_relay)
+ dhcpv4_relay_send(ses->serv->dhcpv4_relay, pack, 0);
ap_session_terminate(&ses->ses, TERM_USER_REQUEST, 0);
}
+ }
+ pthread_mutex_unlock(&serv->lock);
+}
+
+static int parse_dhcpv4_mask(uint32_t mask)
+{
+ int i;
+
+ for (i = 31; i >= 0 && (mask & (1 << i)); i--);
+
+ return 32 - (i + 1);
+}
+
+static void ipoe_ses_recv_dhcpv4_relay(struct ipoe_session *ses)
+{
+ struct dhcpv4_packet *pack = ses->dhcpv4_relay_reply;
+ struct dhcpv4_option *opt;
+
+ if (conf_verbose) {
+ log_ppp_info2("recv ");
+ dhcpv4_print_packet(pack, 1, log_ppp_info2);
+ }
+
+ if (pack->msg_type == DHCPOFFER && ses->ses.state == AP_STATE_STARTING) {
+ triton_timer_del(&ses->timer);
+
+ ses->relay_server_id = pack->server_id;
+
+ if (!ses->yiaddr) {
+ ses->yiaddr = pack->hdr->yiaddr;
+ ses->relay_addr = 1;
+ }
+
+ if (!ses->siaddr) {
+ opt = dhcpv4_packet_find_opt(pack, 3);
+ if (opt)
+ ses->siaddr = *(in_addr_t *)opt->data;
+ }
+
+ opt = dhcpv4_packet_find_opt(pack, 51);
+ if (opt)
+ ses->lease_time = ntohl(*(uint32_t *)opt->data);
+
+ opt = dhcpv4_packet_find_opt(pack, 1);
+ if (opt)
+ ses->mask = parse_dhcpv4_mask(ntohl(*(uint32_t *)opt->data));
+
+ __ipoe_session_start(ses);
+ } else if (pack->msg_type == DHCPACK) {
+ if (ses->ses.state == AP_STATE_STARTING)
+ __ipoe_session_activate(ses);
+ else
+ dhcpv4_send_reply(DHCPACK, ses->serv->dhcpv4, ses->dhcpv4_request, ses->yiaddr, ses->siaddr, ses->mask, ses->lease_time, ses->dhcpv4_relay_reply);
+
+ } else if (pack->msg_type == DHCPNAK) {
+ dhcpv4_send_nak(ses->serv->dhcpv4, ses->dhcpv4_request);
+ ap_session_terminate(&ses->ses, TERM_NAS_REQUEST, 0);
+ return;
+ }
+
+ dhcpv4_packet_free(ses->dhcpv4_relay_reply);
+ ses->dhcpv4_relay_reply = NULL;
+}
+
+static void ipoe_recv_dhcpv4_relay(struct dhcpv4_packet *pack)
+{
+ struct ipoe_serv *serv = container_of(triton_context_self(), typeof(*serv), ctx);
+ struct ipoe_session *ses;
+ int found = 0;
+ //struct dhcpv4_packet *reply;
+
+ if (ap_shutdown) {
dhcpv4_packet_free(pack);
+ return;
+ }
+
+ pthread_mutex_lock(&serv->lock);
+ list_for_each_entry(ses, &serv->sessions, entry) {
+ if (ses->xid != pack->hdr->xid)
+ continue;
+ if (memcmp(ses->hwaddr, pack->hdr->chaddr, 6))
+ continue;
+
+ found = 1;
+ break;
}
+
+ if (found && !ses->dhcpv4_relay_reply) {
+ ses->dhcpv4_relay_reply = pack;
+ triton_context_call(&ses->ctx, (triton_event_func)ipoe_ses_recv_dhcpv4_relay, ses);
+ } else
+ dhcpv4_packet_free(pack);
+
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;
@@ -908,7 +1057,7 @@ static void ev_radius_coa(struct ev_radius_t *ev)
}
//if (l4_redirect && !ses->l4_redirect) || (!l4_redirect && ses->l4_redirect))
- if (l4_redirect != ses->l4_redirect)
+ if (l4_redirect != ses->l4_redirect && ev->ses->state == AP_STATE_ACTIVE)
ipoe_change_l4_redirect(ses, l4_redirect);
}
#endif
@@ -927,6 +1076,11 @@ static void ipoe_serv_close(struct triton_context_t *ctx)
if (serv->dhcpv4)
dhcpv4_free(serv->dhcpv4);
+
+ if (serv->dhcpv4_relay) {
+ ipoe_serv_del_addr(serv, serv->dhcpv4_relay->giaddr);
+ dhcpv4_relay_free(serv->dhcpv4_relay, &serv->ctx);
+ }
triton_context_unregister(ctx);
@@ -983,7 +1137,7 @@ struct ipoe_serv *ipoe_find_serv(const char *ifname)
static void add_interface(const char *ifname, int ifindex, const char *opt)
{
- char *str0, *str, *ptr1, *ptr2;
+ char *str0 = NULL, *str, *ptr1, *ptr2;
int end;
struct ipoe_serv *serv;
int opt_shared = conf_shared;
@@ -991,6 +1145,10 @@ static void add_interface(const char *ifname, int ifindex, const char *opt)
int opt_up = 0;
int opt_mode = conf_mode;
int opt_ifcfg = conf_ifcfg;
+ const char *opt_relay = conf_relay;
+ const char *opt_giaddr = NULL;
+ in_addr_t relay_addr = 0;
+ in_addr_t giaddr = 0;
str0 = strchr(opt, ',');
if (str0) {
@@ -1033,6 +1191,12 @@ static void add_interface(const char *ifname, int ifindex, const char *opt)
goto parse_err;
} else if (strcmp(str, "ifcfg") == 0) {
opt_ifcfg = atoi(ptr1);
+ } else if (strcmp(str, "relay") == 0) {
+ opt_relay = ptr1;
+ relay_addr = inet_addr(ptr1);
+ } else if (strcmp(str, "giaddr") == 0) {
+ opt_giaddr = ptr1;
+ giaddr = inet_addr(ptr1);
}
if (end)
@@ -1040,9 +1204,7 @@ static void add_interface(const char *ifname, int ifindex, const char *opt)
str = ptr2 + 1;
}
-
- _free(str0);
- }
+ }
if (!opt_up && !opt_dhcpv4) {
opt_up = conf_up;
@@ -1074,6 +1236,20 @@ static void add_interface(const char *ifname, int ifindex, const char *opt)
serv->opt_mode = opt_mode;
serv->opt_ifcfg = opt_ifcfg;
+ if (serv->dhcpv4_relay &&
+ (serv->dhcpv4_relay->addr != relay_addr || serv->dhcpv4_relay->giaddr != giaddr)) {
+ ipoe_serv_del_addr(serv, serv->dhcpv4_relay->giaddr);
+ dhcpv4_relay_free(serv->dhcpv4_relay, &serv->ctx);
+ serv->dhcpv4_relay = NULL;
+ }
+
+ if (serv->opt_dhcpv4 && opt_relay && opt_giaddr)
+ ipoe_serv_add_addr(serv, serv->dhcpv4_relay->giaddr);
+ serv->dhcpv4_relay = dhcpv4_relay_create(opt_relay, opt_giaddr, &serv->ctx, (triton_event_func)ipoe_recv_dhcpv4_relay);
+
+ if (str0)
+ _free(str0);
+
return;
}
@@ -1098,12 +1274,20 @@ static void add_interface(const char *ifname, int ifindex, const char *opt)
serv->dhcpv4 = dhcpv4_create(&serv->ctx, serv->ifname, opt);
if (serv->dhcpv4)
serv->dhcpv4->recv = ipoe_recv_dhcpv4;
+
+ if (opt_relay && opt_giaddr) {
+ ipoe_serv_add_addr(serv, giaddr);
+ serv->dhcpv4_relay = dhcpv4_relay_create(opt_relay, opt_giaddr, &serv->ctx, (triton_event_func)ipoe_recv_dhcpv4_relay);
+ }
}
triton_context_wakeup(&serv->ctx);
list_add_tail(&serv->entry, &serv_list);
+ if (str0)
+ _free(str0);
+
return;
parse_err:
@@ -1274,6 +1458,7 @@ static void parse_conf_rad_attr(const char *opt, int *val)
} else
*val = -1;
}
+
static void load_radius_attrs(void)
{
parse_conf_rad_attr("attr-dhcp-client-ip", &conf_attr_dhcp_client_ip);
@@ -1330,7 +1515,7 @@ static void load_config(void)
if (opt)
conf_lease_time = atoi(opt);
- opt = conf_get_opt("ipoe", "lease-timeout");
+ opt = conf_get_opt("ipoe", "max-lease-time");
if (opt)
conf_lease_timeout = atoi(opt);
@@ -1366,6 +1551,8 @@ static void load_config(void)
log_emerg("ipoe: failed to parse 'mode=%s'\n", opt);
} else
conf_mode = MODE_L2;
+
+ conf_relay = conf_get_opt("ipoe", "relay");
conf_dhcpv4 = 0;
conf_up = 0;
@@ -1403,9 +1590,10 @@ static void ipoe_init(void)
triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config);
#ifdef RADIUS
- if (triton_module_loaded("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
}
diff --git a/accel-pppd/ctrl/ipoe/ipoe.h b/accel-pppd/ctrl/ipoe/ipoe.h
index b955b952..2d99ca62 100644
--- a/accel-pppd/ctrl/ipoe/ipoe.h
+++ b/accel-pppd/ctrl/ipoe/ipoe.h
@@ -18,6 +18,7 @@ struct ipoe_serv
struct list_head sessions;
struct list_head addr_list;
struct dhcpv4_serv *dhcpv4;
+ struct dhcpv4_relay *dhcpv4_relay;
pthread_mutex_t lock;
int opt_mode;
int opt_shared:1;
@@ -27,12 +28,6 @@ struct ipoe_serv
int need_close:1;
};
-struct dhcp_opt
-{
- uint8_t len;
- uint8_t data[0];
-};
-
struct ipoe_session
{
struct list_head entry;
@@ -42,20 +37,26 @@ struct ipoe_session
struct ap_ctrl ctrl;
struct ap_session ses;
uint8_t hwaddr[6];
- struct dhcp_opt *client_id;
- struct dhcp_opt *agent_circuit_id;
- struct dhcp_opt *agent_remote_id;
+ struct dhcpv4_option *client_id;
+ struct dhcpv4_option *relay_agent;
+ uint8_t *agent_circuit_id;
+ uint8_t *agent_remote_id;
uint32_t xid;
uint32_t giaddr;
uint32_t yiaddr;
uint32_t siaddr;
+ uint32_t relay_server_id;
int mask;
+ int lease_time;
uint8_t *data;
struct dhcpv4_packet *dhcpv4_request;
+ struct dhcpv4_packet *dhcpv4_relay_reply;
int ifindex;
int ifcfg:1;
int dhcp_addr:1;
+ int relay_addr:1;
int l4_redirect:1;
+ int l4_redirect_set:1;
};
struct ipoe_session_info
diff --git a/accel-pppd/ctrl/ipoe/lua.c b/accel-pppd/ctrl/ipoe/lua.c
index 0b24635f..dbc87e21 100644
--- a/accel-pppd/ctrl/ipoe/lua.c
+++ b/accel-pppd/ctrl/ipoe/lua.c
@@ -146,7 +146,7 @@ static int packet4_agent_circuit_id(lua_State *L)
return 0;
if (ses->agent_circuit_id)
- lua_pushlstring(L, (char *)ses->agent_circuit_id->data, ses->agent_circuit_id->len);
+ lua_pushlstring(L, (char *)(ses->agent_circuit_id + 1), *ses->agent_circuit_id);
else
lua_pushnil(L);
@@ -161,7 +161,7 @@ static int packet4_agent_remote_id(lua_State *L)
return 0;
if (ses->agent_remote_id)
- lua_pushlstring(L, (char *)ses->agent_remote_id->data, ses->agent_remote_id->len);
+ lua_pushlstring(L, (char *)(ses->agent_remote_id + 1), *ses->agent_remote_id);
else
lua_pushnil(L);