summaryrefslogtreecommitdiff
path: root/accel-pppd/ppp/ipv6cp_opt_intfid.c
diff options
context:
space:
mode:
authorKozlov Dmitry <dima@server>2011-08-22 12:33:21 +0400
committerKozlov Dmitry <dima@server>2011-08-22 12:33:21 +0400
commit6815ca31e970c2a8bd1a7b630293dd8313782df8 (patch)
treeac8881b68f56c25883e9db7fb3d94f60496cc215 /accel-pppd/ppp/ipv6cp_opt_intfid.c
parenta93e43804c9a37b3cc2141d5ada3158653a7a221 (diff)
downloadaccel-ppp-6815ca31e970c2a8bd1a7b630293dd8313782df8.tar.gz
accel-ppp-6815ca31e970c2a8bd1a7b630293dd8313782df8.zip
futher ipv6 work
Diffstat (limited to 'accel-pppd/ppp/ipv6cp_opt_intfid.c')
-rw-r--r--accel-pppd/ppp/ipv6cp_opt_intfid.c279
1 files changed, 279 insertions, 0 deletions
diff --git a/accel-pppd/ppp/ipv6cp_opt_intfid.c b/accel-pppd/ppp/ipv6cp_opt_intfid.c
new file mode 100644
index 00000000..8e6675cf
--- /dev/null
+++ b/accel-pppd/ppp/ipv6cp_opt_intfid.c
@@ -0,0 +1,279 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+#include <endian.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include "linux_ppp.h"
+
+#include <netlink/netlink.h>
+
+#include "log.h"
+#include "events.h"
+#include "ppp.h"
+#include "ppp_ipv6cp.h"
+#include "ipdb.h"
+
+#include "memdebug.h"
+
+#define INTF_ID_FIXED 0
+#define INTF_ID_RANDOM 1
+
+static int conf_check_exists;
+static int conf_intf_id = INTF_ID_FIXED;
+static uint64_t conf_intf_id_val = 1;
+
+// from /usr/include/linux/ipv6.h
+struct in6_ifreq {
+ struct in6_addr ifr6_addr;
+ __u32 ifr6_prefixlen;
+ int ifr6_ifindex;
+};
+
+static int urandom_fd;
+static int sock6_fd;
+
+static struct ipv6cp_option_t *ipaddr_init(struct ppp_ipv6cp_t *ipv6cp);
+static void ipaddr_free(struct ppp_ipv6cp_t *ipv6cp, struct ipv6cp_option_t *opt);
+static int ipaddr_send_conf_req(struct ppp_ipv6cp_t *ipv6cp, struct ipv6cp_option_t *opt, uint8_t *ptr);
+static int ipaddr_send_conf_nak(struct ppp_ipv6cp_t *ipv6cp, struct ipv6cp_option_t *opt, uint8_t *ptr);
+static int ipaddr_recv_conf_req(struct ppp_ipv6cp_t *ipv6cp, struct ipv6cp_option_t *opt, uint8_t *ptr);
+//static int ipaddr_recv_conf_ack(struct ppp_ipv6cp_t *ipv6cp, struct ipv6cp_option_t *opt, uint8_t *ptr);
+static void ipaddr_print(void (*print)(const char *fmt,...),struct ipv6cp_option_t*, uint8_t *ptr);
+
+struct ipaddr_option_t
+{
+ struct ipv6cp_option_t opt;
+ uint64_t intf_id;
+ struct ipv6db_item_t *ip;
+ int started:1;
+};
+
+static struct ipv6cp_option_handler_t ipaddr_opt_hnd =
+{
+ .init = ipaddr_init,
+ .send_conf_req = ipaddr_send_conf_req,
+ .send_conf_nak = ipaddr_send_conf_nak,
+ .recv_conf_req = ipaddr_recv_conf_req,
+ .free = ipaddr_free,
+ .print = ipaddr_print,
+};
+
+static struct ipv6cp_option_t *ipaddr_init(struct ppp_ipv6cp_t *ipv6cp)
+{
+ struct ipaddr_option_t *ipaddr_opt = _malloc(sizeof(*ipaddr_opt));
+
+ memset(ipaddr_opt, 0, sizeof(*ipaddr_opt));
+
+ ipaddr_opt->opt.id = CI_INTFID;
+ ipaddr_opt->opt.len = 10;
+
+ switch (conf_intf_id) {
+ case INTF_ID_FIXED:
+ ipaddr_opt->intf_id = conf_intf_id_val;
+ break;
+ case INTF_ID_RANDOM:
+ read(urandom_fd, &ipaddr_opt->intf_id, 8);
+ break;
+ }
+
+ return &ipaddr_opt->opt;
+}
+
+static void ipaddr_free(struct ppp_ipv6cp_t *ipv6cp, struct ipv6cp_option_t *opt)
+{
+ struct ipaddr_option_t *ipaddr_opt=container_of(opt,typeof(*ipaddr_opt),opt);
+
+ _free(ipaddr_opt);
+}
+
+static int check_exists(struct ppp_t *self_ppp, struct in6_addr *addr)
+{
+ struct ppp_t *ppp;
+ int r = 0;
+
+ pthread_rwlock_rdlock(&ppp_lock);
+ list_for_each_entry(ppp, &ppp_list, entry) {
+ if (ppp->terminating)
+ continue;
+ if (ppp == self_ppp)
+ continue;
+
+ if (addr->s6_addr32[0] == ppp->ipv6_addr.s6_addr32[0] &&
+ addr->s6_addr32[1] == ppp->ipv6_addr.s6_addr32[1]) {
+ log_ppp_warn("ppp:ipv6cp: requested IP already assigned to %s\n", ppp->ifname);
+ r = 1;
+ break;
+ }
+ }
+ pthread_rwlock_unlock(&ppp_lock);
+
+ return r;
+}
+
+static int ipaddr_send_conf_req(struct ppp_ipv6cp_t *ipv6cp, struct ipv6cp_option_t *opt, uint8_t *ptr)
+{
+ struct ipaddr_option_t *ipaddr_opt = container_of(opt, typeof(*ipaddr_opt), opt);
+ struct ipv6cp_opt64_t *opt64 = (struct ipv6cp_opt64_t *)ptr;
+
+ if (!ipaddr_opt->ip) {
+ ipaddr_opt->ip = ipdb_get_ipv6(ipv6cp->ppp);
+ if (!ipaddr_opt->ip) {
+ log_ppp_warn("ppp:ipv6cp: no free IP address\n");
+ return -1;
+ }
+ }
+
+ if (conf_check_exists && check_exists(ipv6cp->ppp, &ipaddr_opt->ip->addr))
+ return -1;
+
+ opt64->hdr.id = CI_INTFID;
+ opt64->hdr.len = 10;
+ opt64->val = ipaddr_opt->intf_id;
+ return 10;
+}
+
+static int ipaddr_send_conf_nak(struct ppp_ipv6cp_t *ipv6cp, struct ipv6cp_option_t *opt, uint8_t *ptr)
+{
+ struct ipaddr_option_t *ipaddr_opt = container_of(opt, typeof(*ipaddr_opt), opt);
+ struct ipv6cp_opt64_t *opt64 = (struct ipv6cp_opt64_t *)ptr;
+ opt64->hdr.id = CI_INTFID;
+ opt64->hdr.len = 10;
+ opt64->val = *(uint64_t *)(&ipaddr_opt->ip->addr.s6_addr32[2]);
+ return 10;
+}
+
+static int ipaddr_recv_conf_req(struct ppp_ipv6cp_t *ipv6cp, struct ipv6cp_option_t *opt, uint8_t *ptr)
+{
+ struct ipaddr_option_t *ipaddr_opt = container_of(opt, typeof(*ipaddr_opt), opt);
+ struct ipv6cp_opt64_t *opt64 = (struct ipv6cp_opt64_t* )ptr;
+ struct ifreq ifr;
+ struct in6_ifreq ifr6;
+
+ if (opt64->hdr.len != 10)
+ return IPV6CP_OPT_REJ;
+
+ if (*(uint64_t *)(&ipaddr_opt->ip->addr.s6_addr32[2]) == opt64->val)
+ goto ack;
+
+ return IPV6CP_OPT_NAK;
+
+ack:
+ if (ipaddr_opt->started)
+ return IPV6CP_OPT_ACK;
+
+ ipaddr_opt->started = 1;
+
+ //ipv6cp->ppp->ipaddr = ipaddr_opt->ip->addr;
+ //ipv6cp->ppp->peer_ipaddr = ipaddr_opt->ip->peer_addr;
+
+ //triton_event_fire(EV_PPP_ACCT_START, ipv6cp->ppp);
+ //if (ipv6cp->ppp->stop_time)
+ // return IPV6CP_OPT_ACK;
+
+ //triton_event_fire(EV_PPP_PRE_UP, ipv6cp->ppp);
+ //if (ipv6cp->ppp->stop_time)
+ // return IPV6CP_OPT_ACK;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, ipv6cp->ppp->ifname);
+
+ if (ioctl(sock_fd, SIOCGIFINDEX, &ifr)) {
+ log_ppp_error("ppp:ipv6cp: ioctl(SIOCGIFINDEX): %s\n", strerror(errno));
+ return IPV6CP_OPT_REJ;
+ }
+
+ memset(&ifr6, 0, sizeof(ifr6));
+ ifr6.ifr6_addr.s6_addr16[0] = htons(0xfe80);
+ *(uint64_t *)(ifr6.ifr6_addr.s6_addr + 8) = ipaddr_opt->intf_id;
+ ifr6.ifr6_prefixlen = 64;
+ ifr6.ifr6_ifindex = ifr.ifr_ifindex;
+
+ if (ioctl(sock6_fd, SIOCSIFADDR, &ifr6)) {
+ log_ppp_error("ppp:ipv6cp: ioctl(SIOCSIFADDR): %s\n", strerror(errno));
+ return IPV6CP_OPT_REJ;
+ }
+
+ return IPV6CP_OPT_ACK;
+}
+
+static void ipaddr_print(void (*print)(const char *fmt,...), struct ipv6cp_option_t *opt, uint8_t *ptr)
+{
+ struct ipaddr_option_t *ipaddr_opt = container_of(opt, typeof(*ipaddr_opt), opt);
+ struct ipv6cp_opt64_t *opt64 = (struct ipv6cp_opt64_t *)ptr;
+ struct in6_addr a;
+
+ if (ptr)
+ *(uint64_t *)(a.s6_addr + 8) = opt64->val;
+ else
+ *(uint64_t *)(a.s6_addr + 8) = ipaddr_opt->intf_id;
+
+ print("<addr %x:%x:%x:%x>", ntohs(a.s6_addr16[4]), ntohs(a.s6_addr16[5]), ntohs(a.s6_addr16[6]), ntohs(a.s6_addr16[7]));
+}
+
+static uint64_t parse_intfid(const char *opt)
+{
+ union {
+ uint64_t u64;
+ uint16_t u16[4];
+ } u;
+
+ int n[4];
+ int i;
+
+ if (sscanf(opt, "%x:%x:%x:%x", &n[0], &n[1], &n[2], &n[3]) != 4)
+ goto err;
+
+ for (i = 0; i < 4; i++) {
+ if (n[i] < 0 || n[i] > 0xffff)
+ goto err;
+ u.u16[i] = htons(n[i]);
+ }
+
+ return u.u64;
+
+err:
+ log_error("ppp:ipv6cp: failed to parse ipv6-intf-id\n");
+ conf_intf_id = INTF_ID_RANDOM;
+ return 0;
+}
+
+static void load_config(void)
+{
+ const char *opt;
+
+ opt = conf_get_opt("ppp", "check-ip");
+ if (opt && atoi(opt) > 0)
+ conf_check_exists = 1;
+
+ opt = conf_get_opt("ppp", "ipv6-intf-id");
+ if (opt) {
+ if (!strcmp(opt, "random"))
+ conf_intf_id = INTF_ID_RANDOM;
+ else {
+ conf_intf_id = INTF_ID_FIXED;
+ conf_intf_id_val = parse_intfid(opt);
+ }
+ }
+}
+
+static void init()
+{
+ sock6_fd = socket(AF_INET6, SOCK_DGRAM, 0);
+ if (!sock6_fd) {
+ log_warn("ppp:ipv6cp: kernel doesn't support ipv6\n");
+ return;
+ }
+
+ urandom_fd = open("/dev/urandom", O_RDONLY);
+
+ ipv6cp_option_register(&ipaddr_opt_hnd);
+ load_config();
+ triton_event_register_handler(EV_CONFIG_RELOAD, (triton_event_func)load_config);
+}
+
+DEFINE_INIT(5, init);
+