ipv6cp_opt_intfid.c 8.4 KB
Newer Older
K
Kozlov Dmitry 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include "linux_ppp.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
D
Dmitry Kozlov 已提交
21 22
#define INTF_ID_CSID   2
#define INTF_ID_IPV4   3
K
Kozlov Dmitry 已提交
23 24 25 26

static int conf_check_exists;
static int conf_intf_id = INTF_ID_FIXED;
static uint64_t conf_intf_id_val = 1;
D
Dmitry Kozlov 已提交
27 28
static int conf_peer_intf_id = INTF_ID_FIXED;
static uint64_t conf_peer_intf_id_val = 2;
K
Kozlov Dmitry 已提交
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

// 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;
	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);
}

93
static int check_exists(struct ppp_t *self_ppp)
K
Kozlov Dmitry 已提交
94 95
{
	struct ppp_t *ppp;
96
	struct ipv6db_addr_t *a1, *a2;
K
Kozlov Dmitry 已提交
97 98 99 100 101 102
	int r = 0;

	pthread_rwlock_rdlock(&ppp_lock);
	list_for_each_entry(ppp, &ppp_list, entry) {
		if (ppp->terminating)
			continue;
103 104
		if (!ppp->ipv6)
			continue;
K
Kozlov Dmitry 已提交
105 106 107
		if (ppp == self_ppp)
			continue;

108 109 110 111 112 113 114 115 116
		list_for_each_entry(a1, &ppp->ipv6->addr_list, entry) {
			list_for_each_entry(a2, &self_ppp->ipv6->addr_list, entry) {
				if (a1->addr.s6_addr32[0] == a2->addr.s6_addr32[0] &&
						a1->addr.s6_addr32[1] == a2->addr.s6_addr32[1]) {
					log_ppp_warn("ppp: requested IPv6 address already assigned to %s\n", ppp->ifname);
					r = 1;
					goto out;
				}
			}
K
Kozlov Dmitry 已提交
117 118
		}
	}
119
out:
K
Kozlov Dmitry 已提交
120 121 122 123 124
	pthread_rwlock_unlock(&ppp_lock);

	return r;
}

D
Dmitry Kozlov 已提交
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
static uint64_t generate_peer_intf_id(struct ppp_t *ppp)
{
	char str[4];
	int i, n;
	union {
		uint64_t intf_id;
		uint16_t addr16[4];
	} u;
	
	switch (conf_peer_intf_id) {
		case INTF_ID_FIXED:
			return conf_peer_intf_id_val;
			break;
		case INTF_ID_RANDOM:
			read(urandom_fd, &u, sizeof(u));
			break;
		case INTF_ID_CSID:
			break;
		case INTF_ID_IPV4:
			if (ppp->ipv4) {
				for (i = 0; i < 4; i++) {
					sprintf(str, "%i", (ppp->ipv4->peer_addr >> (i*8)) & 0xff);
					sscanf(str, "%x", &n);
					u.addr16[i] = htons(n);
				}
			} else
				read(urandom_fd, &u, sizeof(u));
	}

	return u.intf_id;
}

K
Kozlov Dmitry 已提交
157 158 159 160 161
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;
	
162 163 164 165
	if (!ipv6cp->ppp->ipv6) {
		ipv6cp->ppp->ipv6 = ipdb_get_ipv6(ipv6cp->ppp);
		if (!ipv6cp->ppp->ipv6) {
			log_ppp_warn("ppp: no free IPv6 address\n");
K
Kozlov Dmitry 已提交
166 167 168
			return -1;
		}
	}
D
Dmitry Kozlov 已提交
169 170 171

	if (!ipv6cp->ppp->ipv6->intf_id)
		ipv6cp->ppp->ipv6->intf_id = generate_peer_intf_id(ipv6cp->ppp);
K
Kozlov Dmitry 已提交
172
	
173
	if (conf_check_exists && check_exists(ipv6cp->ppp))
K
Kozlov Dmitry 已提交
174 175 176 177 178 179 180 181 182 183 184 185 186 187
		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;
188
	opt64->val = ipv6cp->ppp->ipv6->intf_id;
K
Kozlov Dmitry 已提交
189 190 191 192 193 194 195 196
	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 in6_ifreq ifr6;
197
	struct ipv6db_addr_t *a;
K
Kozlov Dmitry 已提交
198 199 200 201

	if (opt64->hdr.len != 10)
		return IPV6CP_OPT_REJ;

202 203
	if (ipv6cp->ppp->ipv6->intf_id == opt64->val) {
		//ipv6cp->delay_ack = ccp_ipcp_started(ipcp->ppp);
K
Kozlov Dmitry 已提交
204
		goto ack;
205
	}
K
Kozlov Dmitry 已提交
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
		
	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;

226 227 228 229 230
	memset(&ifr6, 0, sizeof(ifr6));
	ifr6.ifr6_addr.s6_addr32[0] = htons(0xfe80);
	*(uint64_t *)(ifr6.ifr6_addr.s6_addr + 8) = ipaddr_opt->intf_id;
	ifr6.ifr6_prefixlen = 64;
	ifr6.ifr6_ifindex = ipv6cp->ppp->ifindex;
K
Kozlov Dmitry 已提交
231

232 233
	if (ioctl(sock6_fd, SIOCSIFADDR, &ifr6)) {
		log_ppp_error("ppp:ipv6cp: ioctl(SIOCSIFADDR): %s\n", strerror(errno));
K
Kozlov Dmitry 已提交
234 235 236
		return IPV6CP_OPT_REJ;
	}

237 238
	list_for_each_entry(a, &ipv6cp->ppp->ipv6->addr_list, entry) {
		memcpy(ifr6.ifr6_addr.s6_addr, a->addr.s6_addr, 8);
K
Kozlov Dmitry 已提交
239

240 241 242 243
		if (ioctl(sock6_fd, SIOCSIFADDR, &ifr6)) {
			log_ppp_error("ppp:ipv6cp: ioctl(SIOCSIFADDR): %s\n", strerror(errno));
			return IPV6CP_OPT_REJ;
		}
K
Kozlov Dmitry 已提交
244 245
	}

246 247 248
	if (ppp_ipv6_nd_start(ipv6cp->ppp, ipaddr_opt->intf_id))
		return IPV6CP_OPT_REJ;

K
Kozlov Dmitry 已提交
249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
	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);
		}
	}
D
Dmitry Kozlov 已提交
310 311 312 313 314 315 316 317 318 319 320 321 322 323
	
	opt = conf_get_opt("ppp", "ipv6-peer-intf-id");
	if (opt) {
		if (!strcmp(opt, "random"))
			conf_peer_intf_id = INTF_ID_RANDOM;
		else if (!strcmp(opt, "calling-sid"))
			conf_peer_intf_id = INTF_ID_CSID;
		else if (!strcmp(opt, "ipv4"))
			conf_peer_intf_id = INTF_ID_IPV4;
		else {
			conf_peer_intf_id = INTF_ID_FIXED;
			conf_peer_intf_id_val = parse_intfid(opt);
		}
	}
K
Kozlov Dmitry 已提交
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
}

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);