diff options
author | Kozlov Dmitry <xeb@mail.ru> | 2012-08-03 14:31:52 +0400 |
---|---|---|
committer | Kozlov Dmitry <xeb@mail.ru> | 2012-08-03 14:31:52 +0400 |
commit | 95586a13a28c93e60f8da6b728b0e7b57cdff5f1 (patch) | |
tree | af7e8c580068974b5b60d564b6fb45b7efef6ba0 /accel-pppd/ctrl/ipoe/dhcpv4.c | |
parent | ff80a772e3116afa4513397db2aa51962ff32994 (diff) | |
download | accel-ppp-xebd-95586a13a28c93e60f8da6b728b0e7b57cdff5f1.tar.gz accel-ppp-xebd-95586a13a28c93e60f8da6b728b0e7b57cdff5f1.zip |
ipoe: implemented dhcp relay
Diffstat (limited to 'accel-pppd/ctrl/ipoe/dhcpv4.c')
-rw-r--r-- | accel-pppd/ctrl/ipoe/dhcpv4.c | 422 |
1 files changed, 344 insertions, 78 deletions
diff --git a/accel-pppd/ctrl/ipoe/dhcpv4.c b/accel-pppd/ctrl/ipoe/dhcpv4.c index 08da43a..86aba87 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; |