diff options
author | Kozlov Dmitry <xeb@mail.ru> | 2012-07-12 19:07:26 +0400 |
---|---|---|
committer | Kozlov Dmitry <xeb@mail.ru> | 2012-07-12 19:07:26 +0400 |
commit | 0df0604e7606b38f1f3fc4a2c92dbad720654f3c (patch) | |
tree | cfe57070ccef10ea4efc8adfa85f868dfc1a2573 /accel-pppd/ctrl/ipoe/dhcpv4.c | |
parent | 22c8cf9724a2766b5f38afbc250065308c2bd7e2 (diff) | |
download | accel-ppp-0df0604e7606b38f1f3fc4a2c92dbad720654f3c.tar.gz accel-ppp-0df0604e7606b38f1f3fc4a2c92dbad720654f3c.zip |
ipoe: per-interface static dhcp pool of addresses
Diffstat (limited to 'accel-pppd/ctrl/ipoe/dhcpv4.c')
-rw-r--r-- | accel-pppd/ctrl/ipoe/dhcpv4.c | 171 |
1 files changed, 162 insertions, 9 deletions
diff --git a/accel-pppd/ctrl/ipoe/dhcpv4.c b/accel-pppd/ctrl/ipoe/dhcpv4.c index 7891181..f1a8876 100644 --- a/accel-pppd/ctrl/ipoe/dhcpv4.c +++ b/accel-pppd/ctrl/ipoe/dhcpv4.c @@ -42,7 +42,54 @@ static mempool_t opt_pool; static int dhcpv4_read(struct triton_md_handler_t *h); -struct dhcpv4_serv *dhcpv4_create(struct triton_context_t *ctx, const char *ifname) +static struct dhcpv4_iprange *parse_range(const char *str) +{ + unsigned int f1,f2,f3,f4,m,n, mask, start, end, len; + struct dhcpv4_iprange *r; + + n = sscanf(str, "%u.%u.%u.%u/%u", &f1, &f2, &f3, &f4, &m); + + if (n != 5) + goto parse_err; + if (f1 > 255) + goto parse_err; + if (f2 > 255) + goto parse_err; + if (f3 > 255) + goto parse_err; + if (f4 > 255) + goto parse_err; + if (m == 0 || m > 30) + goto parse_err; + + start = (f1 << 24) | (f2 << 16) | (f3 << 8) | f4; + mask = ~((1 << (32 - m)) - 1); + start = start & mask; + end = start | ~mask; + + len = (end - start - 1) / (8 * sizeof(long)) + 1; + + r = _malloc(sizeof(*r) + len * sizeof(long)); + memset(r, 0, sizeof(*r)); + memset(r->free, 0xff, len * sizeof(long)); + r->routerip = start + 1; + r->startip = start; + r->mask = m; + r->len = len; + pthread_mutex_init(&r->lock, NULL); + + end -= start; + r->free[(end - 1) / ( 8 * sizeof(long))] &= (1 << ((end - 1) % (8 * sizeof(long)) + 1)) - 1; + r->free[0] &= ~3; + + return r; + +parse_err: + log_emerg("dhcpv4: failed to parse range=%s\n", str); + return NULL; +} + +struct dhcpv4_serv *dhcpv4_create(struct triton_context_t *ctx, const char *ifname, const char *opt) { struct dhcpv4_serv *serv; int sock, raw_sock; @@ -50,6 +97,8 @@ struct dhcpv4_serv *dhcpv4_create(struct triton_context_t *ctx, const char *ifna struct sockaddr_ll ll_addr; struct ifreq ifr; int f = 1; + char *str0, *str, *ptr1, *ptr2; + int end; memset(&ifr, 0, sizeof(ifr)); @@ -124,6 +173,41 @@ struct dhcpv4_serv *dhcpv4_create(struct triton_context_t *ctx, const char *ifna serv->hnd.read = dhcpv4_read; serv->raw_sock = raw_sock; + str0 = strchr(opt, ','); + if (str0) { + str0 = _strdup(str0 + 1); + str = str0; + + while (1) { + for (ptr1 = str + 1; *ptr1 && *ptr1 != '='; ptr1++); + + if (!*ptr1) + break; + + *ptr1 = 0; + + for (ptr2 = ++ptr1; *ptr2 && *ptr2 != ','; ptr2++); + + end = *ptr2 == 0; + + if (!end) + *ptr2 = 0; + + if (ptr2 == ptr1) + break; + + if (strcmp(str, "range") == 0) + serv->range = parse_range(ptr1); + + if (end) + break; + + str = ptr2 + 1; + } + + _free(str0); + } + triton_md_register_handler(ctx, &serv->hnd); triton_md_enable_handler(&serv->hnd, MD_MODE_READ); @@ -139,6 +223,8 @@ void dhcpv4_free(struct dhcpv4_serv *serv) { triton_md_unregister_handler(&serv->hnd); close(serv->hnd.fd); + if (serv->range) + _free(serv->range); _free(serv); } @@ -372,7 +458,7 @@ uint16_t ip_csum(uint16_t *buf, int len) } -static int dhcpv4_send(struct dhcpv4_serv *serv, struct dhcpv4_packet *pack, in_addr_t saddr, in_addr_t daddr) +static int dhcpv4_send_raw(struct dhcpv4_serv *serv, struct dhcpv4_packet *pack, in_addr_t saddr, in_addr_t daddr) { uint8_t hdr[sizeof(struct ether_header) + sizeof(struct iphdr) + sizeof(struct udphdr)]; struct ether_header *eth = (struct ether_header *)hdr; @@ -416,6 +502,32 @@ static int dhcpv4_send(struct dhcpv4_serv *serv, struct dhcpv4_packet *pack, in_ return 0; } +static int dhcpv4_send_udp(struct dhcpv4_serv *serv, struct dhcpv4_packet *pack) +{ + struct sockaddr_in addr; + int n; + int len = pack->ptr - pack->data; + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_port = htons(DHCP_CLIENT_PORT); + addr.sin_addr.s_addr = pack->hdr->giaddr; + + n = sendto(serv->hnd.fd, pack->data, len, 0, (struct sockaddr *)&addr, sizeof(addr)); + if (n != len) + return -1; + + return 0; +} + +static int dhcpv4_send(struct dhcpv4_serv *serv, struct dhcpv4_packet *pack, in_addr_t saddr, in_addr_t daddr) +{ + if (pack->hdr->giaddr) + return dhcpv4_send_udp(serv, pack); + + return dhcpv4_send_raw(serv, pack, saddr, daddr); +} + void dhcpv4_packet_free(struct dhcpv4_packet *pack) { struct dhcpv4_option *opt; @@ -459,7 +571,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, struct ap_session *ses, 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 *pack; int val, r; @@ -478,26 +590,26 @@ int dhcpv4_send_reply(int msg_type, struct dhcpv4_serv *serv, struct dhcpv4_pack pack->hdr->op = DHCP_OP_REPLY; pack->hdr->ciaddr = 0; - pack->hdr->yiaddr = ses->ipv4->peer_addr; + pack->hdr->yiaddr = yiaddr; if (msg_type == DHCPOFFER) - pack->hdr->siaddr = ses->ipv4->addr; + pack->hdr->siaddr = siaddr; else pack->hdr->siaddr = 0; if (dhcpv4_packet_add_opt(pack, 53, &msg_type, 1)) goto out_err; - if (dhcpv4_packet_add_opt(pack, 54, &ses->ipv4->addr, 4)) + if (dhcpv4_packet_add_opt(pack, 54, &siaddr, 4)) goto out_err; val = ntohl(lease_time); if (dhcpv4_packet_add_opt(pack, 51, &val, 4)) goto out_err; - if (dhcpv4_packet_add_opt(pack, 3, &ses->ipv4->addr, 4)) + if (dhcpv4_packet_add_opt(pack, 3, &siaddr, 4)) goto out_err; - val = htonl(~((1 << (32 - ses->ipv4->mask)) - 1)); + val = htonl(~((1 << (32 - mask)) - 1)); if (dhcpv4_packet_add_opt(pack, 1, &val, 4)) goto out_err; @@ -519,7 +631,7 @@ int dhcpv4_send_reply(int msg_type, struct dhcpv4_serv *serv, struct dhcpv4_pack dhcpv4_print_packet(pack, log_ppp_info2); } - r = dhcpv4_send(serv, pack, ses->ipv4->addr, ses->ipv4->peer_addr); + r = dhcpv4_send(serv, pack, siaddr, yiaddr); dhcpv4_packet_free(pack); @@ -573,6 +685,47 @@ out_err: return 0; } +int dhcpv4_get_ip(struct dhcpv4_serv *serv, uint32_t *yiaddr, uint32_t *siaddr, int *mask) +{ + int i, k; + + if (!serv->range) + return 0; + + pthread_mutex_lock(&serv->range->lock); + + while (1) { + for (i = serv->range->pos; i < serv->range->len; i++) { + k = ffsl(serv->range->free[i]); + if (k) { + serv->range->free[i] &= ~(1 << (k - 1)); + serv->range->pos = i; + pthread_mutex_unlock(&serv->range->lock); + *yiaddr = htonl(serv->range->startip + i * 8 * sizeof(long) + k - 1); + *siaddr = htonl(serv->range->routerip); + *mask = serv->range->mask; + return 1; + } + } + + if (serv->range->pos == 0) + break; + + serv->range->pos = 0; + } + + pthread_mutex_unlock(&serv->range->lock); + return 0; +} + +void dhcpv4_put_ip(struct dhcpv4_serv *serv, uint32_t ip) +{ + int n = ntohl(ip) - serv->range->startip; + pthread_mutex_lock(&serv->range->lock); + serv->range->free[n / (8 * sizeof(long))] |= 1 << (n % (8 * sizeof(long))); + pthread_mutex_unlock(&serv->range->lock); +} + static void load_config() { const char *opt; |