diff --git a/accel-pppd/accel-ppp.conf b/accel-pppd/accel-ppp.conf index 64c69e80e004821d754fc3b89ab891ba36767fa1..0199f058a1b0e35bfc37ea4007e3362a6d11a46f 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 1dc9e859b8680048ce6032984eb2d2b40729cb60..82dd84a8da7c507d749ec9347b031ba421fec223 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 0bc64484738133b6f6debbe28938942587b2f8eb..9633bf71de2d8d63e2c704bffcecc8ea48b99e23 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 08da43ac0d65a068afdaad40381a86c2f542f08f..86aba875e05ba129c83ca06539feb88838b4ff6a 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 3ebb74cc615e5d82ec59171403bbb01f7134e1ce..67f7e61a17ee47a7c573dd1d8e7829de61a8cff9 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 711dca3997dbd4df9e9e5fd584f3c9f8bb631875..14e8d37539f45bd32f3896d3c15e30c46762ec0b 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 b955b952ed0f9850fd02e8b2ad87b8a300a9bc9f..2d99ca6249da2fdf58b5b90f9c8a1ebe21163d26 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 0b24635fb9336c78f65d4c1e3f2f3efb461f24f4..dbc87e21076c5678138395ac8aa87c491f9dfa90 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);