diff options
Diffstat (limited to 'accel-pppd/ctrl/ipoe')
-rw-r--r-- | accel-pppd/ctrl/ipoe/arp.c | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/accel-pppd/ctrl/ipoe/arp.c b/accel-pppd/ctrl/ipoe/arp.c new file mode 100644 index 0000000..fbef3d9 --- /dev/null +++ b/accel-pppd/ctrl/ipoe/arp.c @@ -0,0 +1,181 @@ +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <time.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <net/ethernet.h> +#include <netinet/ip.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <linux/if_arp.h> +#include <linux/if_packet.h> + +#include "list.h" +#include "triton.h" +#include "log.h" + +#include "ipoe.h" + +#include "memdebug.h" + +struct _arphdr { + __be16 ar_hrd; + __be16 ar_pro; + __u8 ar_hln; + __u8 ar_pln; + __be16 ar_op; + __u8 ar_sha[ETH_ALEN]; + __be32 ar_spa; + __u8 ar_tha[ETH_ALEN]; + __be32 ar_tpa; +} __packed; + +static int arp_read(struct triton_md_handler_t *h) +{ + struct arp_serv *s = container_of(h, typeof(*s), h); + char buf[128]; + int n; + struct _arphdr *ah = (struct _arphdr *)buf; + struct _arphdr ah2; + struct sockaddr_ll src, dst; + socklen_t slen = sizeof(src); + struct ipoe_session *ses, *ses1, *ses2; + + memset(&dst, 0, sizeof(dst)); + dst.sll_family = AF_PACKET; + dst.sll_ifindex = s->ipoe->ifindex; + dst.sll_protocol = htons(ETH_P_ARP); + + ah2.ar_hrd = htons(ARPHRD_ETHER); + ah2.ar_pro = htons(ETH_P_IP); + ah2.ar_hln = ETH_ALEN; + ah2.ar_pln = 4; + ah2.ar_op = htons(ARPOP_REPLY); + + while (1) { + n = recvfrom(h->fd, buf, sizeof(buf), MSG_DONTWAIT, (struct sockaddr *)&src, &slen); + if (n < 0) { + if (errno == EAGAIN) + break; + continue; + } + + if (n < sizeof(*ah)) + continue; + + if (ah->ar_op != htons(ARPOP_REQUEST)) + continue; + + if (ah->ar_pln != 4) + continue; + + if (ah->ar_pro != htons(ETH_P_IP)) + continue; + + if (ah->ar_hln != ETH_ALEN) + continue; + + if (memcmp(ah->ar_sha, src.sll_addr, ETH_ALEN)) + continue; + + ses1 = ses2 = NULL; + pthread_mutex_lock(&s->ipoe->lock); + list_for_each_entry(ses, &s->ipoe->sessions, entry) { + if (ses->yiaddr == ah->ar_spa) { + ses1 = ses; + if (ses->ses.state != AP_STATE_ACTIVE) + break; + } + if (ses->yiaddr == ah->ar_tpa) { + ses2 = ses; + if (ses->ses.state != AP_STATE_ACTIVE) + break; + } + if (ses1 && ses2) + break; + } + + if ((ses1 && ses1->ses.state != AP_STATE_ACTIVE) || + (ses2 && ses2->ses.state != AP_STATE_ACTIVE)) { + pthread_mutex_unlock(&s->ipoe->lock); + continue; + } + + if (!ses1) + memcpy(ah2.ar_sha, s->ipoe->hwaddr, ETH_ALEN); + else if (ses2) { + if (s->ipoe->opt_arp == 1 || ses1 == ses2) + continue; + memcpy(ah2.ar_sha, ses2->hwaddr, ETH_ALEN); + } else + memcpy(ah2.ar_sha, s->ipoe->hwaddr, ETH_ALEN); + + pthread_mutex_unlock(&s->ipoe->lock); + + memcpy(dst.sll_addr, ah->ar_sha, ETH_ALEN); + memcpy(ah2.ar_tha, ah->ar_sha, ETH_ALEN); + ah2.ar_spa = ah->ar_tpa; + ah2.ar_tpa = ah->ar_spa; + + sendto(h->fd, &ah2, sizeof(ah2), MSG_DONTWAIT, (struct sockaddr *)&dst, sizeof(dst)); + } + + return 0; +} + +struct arp_serv *arpd_start(struct ipoe_serv *ipoe) +{ + int sock; + struct sockaddr_ll addr; + struct arp_serv *s; + int f = 1; + + sock = socket(PF_PACKET, SOCK_DGRAM, 0); + if (sock < 0) { + log_error("ipoe: arp: socket: %s\n", strerror(errno)); + return NULL; + } + + memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_protocol = htons(ETH_P_ARP); + addr.sll_ifindex = ipoe->ifindex; + + if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &f, sizeof(f))) { + log_error("ipoe: setsockopt(SO_BROADCAST): %s\n", strerror(errno)); + close(sock); + return NULL; + } + + if (bind(sock, (struct sockaddr *)&addr, sizeof(addr))) { + log_error("ipoe: arp: bind: %s\n", strerror(errno)); + close(sock); + return NULL; + } + + s = _malloc(sizeof(*s)); + s->ipoe = ipoe; + s->h.fd = sock; + s->h.read = arp_read; + + fcntl(sock, F_SETFL, O_NONBLOCK); + fcntl(sock, F_SETFD, fcntl(sock, F_GETFD) | FD_CLOEXEC); + + triton_md_register_handler(&ipoe->ctx, &s->h); + triton_md_enable_handler(&s->h, MD_MODE_READ); + + return s; +} + +void arpd_stop(struct arp_serv *arp) +{ + triton_md_unregister_handler(&arp->h); + close(arp->h.fd); + _free(arp); +} + |