Beispiel #1
0
static void dhcpv6_send(enum dhcpv6_msg type, uint8_t trid[3], uint32_t ecs)
{
	// Build FQDN
	char fqdn_buf[256];
	gethostname(fqdn_buf, sizeof(fqdn_buf));
	struct {
		uint16_t type;
		uint16_t len;
		uint8_t flags;
		uint8_t data[256];
	} fqdn;
	size_t fqdn_len = 5 + dn_comp(fqdn_buf, fqdn.data,
			sizeof(fqdn.data), NULL, NULL);
	fqdn.type = htons(DHCPV6_OPT_FQDN);
	fqdn.len = htons(fqdn_len - 4);
	fqdn.flags = 0;


	// Build Client ID
	size_t cl_id_len;
	void *cl_id = odhcp6c_get_state(STATE_CLIENT_ID, &cl_id_len);

	// Get Server ID
	size_t srv_id_len;
	void *srv_id = odhcp6c_get_state(STATE_SERVER_ID, &srv_id_len);

	// Build IA_PDs
	size_t ia_pd_entries = 0, ia_pd_len = 0;
	uint8_t *ia_pd;

	if (type == DHCPV6_MSG_SOLICIT) {
		odhcp6c_clear_state(STATE_IA_PD);
		size_t n_prefixes;
		struct odhcp6c_request_prefix *request_prefixes = odhcp6c_get_state(STATE_IA_PD_INIT, &n_prefixes);
		n_prefixes /= sizeof(struct odhcp6c_request_prefix);

		ia_pd = alloca(n_prefixes * (sizeof(struct dhcpv6_ia_hdr) + sizeof(struct dhcpv6_ia_prefix)));

		for (size_t i = 0; i < n_prefixes; i++) {
			struct dhcpv6_ia_hdr hdr_ia_pd = {
				htons(DHCPV6_OPT_IA_PD),
				htons(sizeof(hdr_ia_pd) - 4 +
				      sizeof(struct dhcpv6_ia_prefix) * !!request_prefixes[i].length),
				request_prefixes[i].iaid, 0, 0
			};
			struct dhcpv6_ia_prefix pref = {
				.type = htons(DHCPV6_OPT_IA_PREFIX),
				.len = htons(sizeof(pref) - 4),
				.prefix = request_prefixes[i].length
			};
			memcpy(ia_pd + ia_pd_len, &hdr_ia_pd, sizeof(hdr_ia_pd));
			ia_pd_len += sizeof(hdr_ia_pd);
			if (request_prefixes[i].length) {
				memcpy(ia_pd + ia_pd_len, &pref, sizeof(pref));
				ia_pd_len += sizeof(pref);
			}
		}
	} else {
Beispiel #2
0
				request_prefixes[i].iaid, 0, 0
			};
			struct dhcpv6_ia_prefix pref = {
				.type = htons(DHCPV6_OPT_IA_PREFIX),
				.len = htons(sizeof(pref) - 4),
				.prefix = request_prefixes[i].length
			};
			memcpy(ia_pd + ia_pd_len, &hdr_ia_pd, sizeof(hdr_ia_pd));
			ia_pd_len += sizeof(hdr_ia_pd);
			if (request_prefixes[i].length) {
				memcpy(ia_pd + ia_pd_len, &pref, sizeof(pref));
				ia_pd_len += sizeof(pref);
			}
		}
	} else {
		struct odhcp6c_entry *e = odhcp6c_get_state(STATE_IA_PD, &ia_pd_entries);
		ia_pd_entries /= sizeof(*e);

		// we're too lazy to count our distinct IAIDs,
		// so just allocate maximally needed space
		ia_pd = alloca(ia_pd_entries * (sizeof(struct dhcpv6_ia_prefix) + 10 +
					sizeof(struct dhcpv6_ia_hdr)));

		for (size_t i = 0; i < ia_pd_entries; ++i) {
			uint32_t iaid = e[i].iaid;

			// check if this is an unprocessed IAID and skip if not.
			int new_iaid = 1;
			for (int j = i-1; j >= 0; j--) {
				if (e[j].iaid == iaid) {
					new_iaid = 0;
Beispiel #3
0
int init_dhcpv6(const char *ifname, unsigned int options, int sol_timeout)
{
	client_options = options;
	dhcpv6_retx[DHCPV6_MSG_SOLICIT].max_timeo = sol_timeout;

#ifdef SOCK_CLOEXEC
	sock = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP);
#else
	sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
	sock = fflags(sock, O_CLOEXEC);
#endif
	if (sock < 0)
		return -1;

	// Detect interface
	struct ifreq ifr;
	strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
	if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0)
		return -1;
	ifindex = ifr.ifr_ifindex;

	// Create client DUID
	size_t client_id_len;
	odhcp6c_get_state(STATE_CLIENT_ID, &client_id_len);
	if (client_id_len == 0) {
		uint8_t duid[14] = {0, DHCPV6_OPT_CLIENTID, 0, 10, 0,
				DHCPV6_DUID_LLADDR, 0, 1};

		if (ioctl(sock, SIOCGIFHWADDR, &ifr) >= 0)
			memcpy(&duid[8], ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);

		uint8_t zero[ETHER_ADDR_LEN] = {0, 0, 0, 0, 0, 0};
		struct ifreq ifs[100], *ifp, *ifend;
		struct ifconf ifc;
		ifc.ifc_req = ifs;
		ifc.ifc_len = sizeof(ifs);

		if (!memcmp(&duid[8], zero, ETHER_ADDR_LEN) &&
				ioctl(sock, SIOCGIFCONF, &ifc) >= 0) {
			// If our interface doesn't have an address...
			ifend = ifs + (ifc.ifc_len / sizeof(struct ifreq));
			for (ifp = ifc.ifc_req; ifp < ifend &&
					!memcmp(&duid[8], zero, ETHER_ADDR_LEN); ifp++) {
				memcpy(ifr.ifr_name, ifp->ifr_name,
						sizeof(ifr.ifr_name));
				if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0)
					continue;

				memcpy(&duid[8], ifr.ifr_hwaddr.sa_data,
						ETHER_ADDR_LEN);
			}
		}

		odhcp6c_add_state(STATE_CLIENT_ID, duid, sizeof(duid));
	}

	// Create ORO
	if (!(client_options & DHCPV6_STRICT_OPTIONS)) {
		uint16_t oro[] = {
			htons(DHCPV6_OPT_SIP_SERVER_D),
			htons(DHCPV6_OPT_SIP_SERVER_A),
			htons(DHCPV6_OPT_DNS_SERVERS),
			htons(DHCPV6_OPT_DNS_DOMAIN),
			htons(DHCPV6_OPT_SNTP_SERVERS),
			htons(DHCPV6_OPT_NTP_SERVER),
			htons(DHCPV6_OPT_AFTR_NAME),
			htons(DHCPV6_OPT_PD_EXCLUDE),
			htons(DHCPV6_OPT_SOL_MAX_RT),
			htons(DHCPV6_OPT_INF_MAX_RT),
#ifdef EXT_CER_ID
			htons(DHCPV6_OPT_CER_ID),
#endif
			htons(DHCPV6_OPT_S46_CONT_MAPE),
			htons(DHCPV6_OPT_S46_CONT_MAPT),
			htons(DHCPV6_OPT_S46_CONT_LW),
		};
		odhcp6c_add_state(STATE_ORO, oro, sizeof(oro));
	}

	// Configure IPv6-options
	int val = 1;
	setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
	setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val));
	setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname));

	struct sockaddr_in6 client_addr = { .sin6_family = AF_INET6,
		.sin6_port = htons(DHCPV6_CLIENT_PORT), .sin6_flowinfo = 0 };
	if (bind(sock, (struct sockaddr*)&client_addr, sizeof(client_addr)) < 0)
		return -1;

	return 0;
}

enum {
	IOV_HDR=0,
	IOV_ORO,
	IOV_ORO_REFRESH,
	IOV_CL_ID,
	IOV_SRV_ID,
	IOV_VENDOR_CLASS_HDR,
	IOV_VENDOR_CLASS,
	IOV_USER_CLASS_HDR,
	IOV_USER_CLASS,
	IOV_RECONF_ACCEPT,
	IOV_FQDN,
	IOV_HDR_IA_NA,
	IOV_IA_NA,
	IOV_IA_PD,
	IOV_TOTAL
};

int dhcpv6_set_ia_mode(enum odhcp6c_ia_mode na, enum odhcp6c_ia_mode pd)
{
	int mode = DHCPV6_UNKNOWN;

	na_mode = na;
	pd_mode = pd;

	if (na_mode == IA_MODE_NONE && pd_mode == IA_MODE_NONE)
		mode = DHCPV6_STATELESS;
	else if (na_mode == IA_MODE_FORCE || pd_mode == IA_MODE_FORCE)
		mode = DHCPV6_STATEFUL;

	return mode;
}
Beispiel #4
0
int main(_unused int argc, char* const argv[])
{
	openlog("odhcp6c", LOG_PERROR | LOG_PID, LOG_DAEMON);

	// Allocate ressources
	const char *pidfile = NULL;
	const char *script = "/usr/sbin/odhcp6c-update";
	ssize_t l;
	uint8_t buf[134];
	char *optpos;
	uint16_t opttype;
	enum odhcp6c_ia_mode ia_na_mode = IA_MODE_TRY;

	bool help = false, daemonize = false;
	int c, request_pd = 0;
	while ((c = getopt(argc, argv, "N:P:c:r:s:p:Sqdh")) != -1) {
		switch (c) {
		case 'S':
			allow_slaac_only = false;
			break;

		case 'N':
			if (!strcmp(optarg, "force"))
				ia_na_mode = IA_MODE_FORCE;
			else if (!strcmp(optarg, "none"))
				ia_na_mode = IA_MODE_NONE;
			else if (!strcmp(optarg, "try"))
				ia_na_mode = IA_MODE_TRY;
			else
				help = true;
			break;

		case 'P':
			allow_slaac_only = false;
			request_pd = strtoul(optarg, NULL, 10);
			if (request_pd == 0)
				request_pd = -1;
			break;

		case 'c':
			l = script_unhexlify(&buf[4], sizeof(buf) - 4, optarg);
			if (l > 0) {
				buf[0] = 0;
				buf[1] = DHCPV6_OPT_CLIENTID;
				buf[2] = 0;
				buf[4] = l;
				odhcp6c_add_state(STATE_CLIENT_ID, buf, l + 4);
			} else {
				help = true;
			}
			break;

		case 'q':
			log_quiet = true;
			break;

		case 'r':
			optpos = optarg;
			while (optpos[0]) {
				opttype = htons(strtoul(optarg, &optpos, 10));
				if (optpos == optarg)
					break;
				else if (optpos[0])
					optarg = &optpos[1];
				odhcp6c_add_state(STATE_ORO, &opttype, 2);
			}
			break;

		case 's':
			script = optarg;
			break;

		case 'd':
			daemonize = true;
			break;

		case 'p':
			pidfile = optarg;
			break;

		default:
			help = true;
			break;
		}
	}

	const char *ifname = argv[optind];

	if (help || !ifname)
		return usage();

	if ((urandom_fd = open("/dev/urandom", O_CLOEXEC | O_RDONLY)) < 0 ||
			init_dhcpv6(ifname, request_pd) || ra_init(ifname) ||
			script_init(script, ifname)) {
		dhcpv6_syslog(LOG_ERR, "failed to initialize: %s", strerror(errno));
		return 3;
	}

	signal(SIGIO, sighandler);
	signal(SIGHUP, sighandler);
	signal(SIGINT, sighandler);
	signal(SIGCHLD, sighandler);
	signal(SIGTERM, sighandler);
	signal(SIGUSR1, sighandler);
	signal(SIGUSR2, sighandler);

	if (daemonize) {
		openlog("odhcp6c", LOG_PID, LOG_DAEMON); // Disable LOG_PERROR
		if (daemon(0, 0)) {
			dhcpv6_syslog(LOG_ERR, "Failed to daemonize: %s",
					strerror(errno));
			return 4;
		}

		char pidbuf[128];
		if (!pidfile) {
			snprintf(pidbuf, sizeof(pidbuf),
					"/var/run/odhcp6c.%s.pid", ifname);
			pidfile = pidbuf;
		}

		int fd = open(pidfile, O_WRONLY | O_CREAT);
		if (fd >= 0) {
			char buf[8];
			int len = snprintf(buf, sizeof(buf), "%i\n", getpid());
			write(fd, buf, len);
			close(fd);
		}
	}

	script_call("started");

	while (do_signal != SIGTERM) { // Main logic
		odhcp6c_clear_state(STATE_SERVER_ID);
		odhcp6c_clear_state(STATE_SERVER_CAND);
		odhcp6c_clear_state(STATE_IA_PD);
		odhcp6c_clear_state(STATE_SNTP_IP);
		odhcp6c_clear_state(STATE_SNTP_FQDN);
		odhcp6c_clear_state(STATE_SIP_IP);
		odhcp6c_clear_state(STATE_SIP_FQDN);
		dhcpv6_set_ia_na_mode(ia_na_mode);
		bound = false;

		dhcpv6_syslog(LOG_NOTICE, "(re)starting transaction on %s", ifname);

		do_signal = 0;
		int res = dhcpv6_request(DHCPV6_MSG_SOLICIT);
		odhcp6c_signal_process();

		if (res < 0) {
			continue; // Might happen if we got a signal
		} else if (res == DHCPV6_STATELESS) { // Stateless mode
			while (do_signal == 0 || do_signal == SIGUSR1) {
				do_signal = 0;

				res = dhcpv6_request(DHCPV6_MSG_INFO_REQ);
				odhcp6c_signal_process();
				if (do_signal == SIGUSR1)
					continue;
				else if (res < 0)
					break;
				else if (res > 0)
					script_call("informed");

				bound = true;
				dhcpv6_syslog(LOG_NOTICE, "entering stateless-mode on %s", ifname);

				if (dhcpv6_poll_reconfigure() > 0)
					script_call("informed");
			}

			continue;
		}

		// Stateful mode
		if (dhcpv6_request(DHCPV6_MSG_REQUEST) < 0)
			continue;

		odhcp6c_signal_process();
		script_call("bound");
		bound = true;
		dhcpv6_syslog(LOG_NOTICE, "entering stateful-mode on %s", ifname);

		while (do_signal == 0 || do_signal == SIGUSR1) {
			// Renew Cycle
			// Wait for T1 to expire or until we get a reconfigure
			int res = dhcpv6_poll_reconfigure();
			odhcp6c_signal_process();
			if (res >= 0) {
				if (res > 0)
					script_call("updated");

				continue;
			}

			// Handle signal, if necessary
			if (do_signal == SIGUSR1)
				do_signal = 0; // Acknowledged
			else if (do_signal > 0)
				break; // Other signal type

			size_t ia_pd_len, ia_na_len, ia_pd_new, ia_na_new;
			odhcp6c_get_state(STATE_IA_PD, &ia_pd_len);
			odhcp6c_get_state(STATE_IA_NA, &ia_na_len);

			// If we have any IAs, send renew, otherwise request
			int r;
			if (ia_pd_len == 0 && ia_na_len == 0)
				r = dhcpv6_request(DHCPV6_MSG_REQUEST);
			else
				r = dhcpv6_request(DHCPV6_MSG_RENEW);
			odhcp6c_signal_process();
			if (r > 0) // Publish updates
				script_call("updated");
			if (r >= 0)
				continue; // Renew was successful

			odhcp6c_clear_state(STATE_SERVER_ID); // Remove binding

			// If we have IAs, try rebind otherwise restart
			res = dhcpv6_request(DHCPV6_MSG_REBIND);
			odhcp6c_signal_process();

			odhcp6c_get_state(STATE_IA_PD, &ia_pd_new);
			odhcp6c_get_state(STATE_IA_NA, &ia_na_new);
			if (res < 0 || (ia_pd_new == 0 && ia_pd_len) ||
					(ia_na_new == 0 && ia_na_len))
				break; // We lost all our IAs, restart
			else if (res > 0)
				script_call("rebound");
		}


		size_t ia_pd_len, ia_na_len, server_id_len;
		odhcp6c_get_state(STATE_IA_PD, &ia_pd_len);
		odhcp6c_get_state(STATE_IA_NA, &ia_na_len);
		odhcp6c_get_state(STATE_SERVER_ID, &server_id_len);

		// Add all prefixes to lost prefixes
		bound = false;
		script_call("unbound");

		if (server_id_len > 0 && (ia_pd_len > 0 || ia_na_len > 0))
			dhcpv6_request(DHCPV6_MSG_RELEASE);

		odhcp6c_clear_state(STATE_IA_NA);
		odhcp6c_clear_state(STATE_IA_PD);
	}

	script_call("stopped");
	return 0;
}
Beispiel #5
0
	memmove(data + offset, data + offset + len, len_after);
	return state_len[state] -= len;
}


void* odhcp6c_get_state(enum odhcp6c_state state, size_t *len)
{
	*len = state_len[state];
	return state_data[state];
}


struct odhcp6c_entry* odhcp6c_find_entry(enum odhcp6c_state state, const struct odhcp6c_entry *new)
{
	size_t len, cmplen = offsetof(struct odhcp6c_entry, target) + new->length / 8;
	struct odhcp6c_entry *start = odhcp6c_get_state(state, &len);
	struct odhcp6c_entry *x = NULL;

	for (struct odhcp6c_entry *c = start; !x && c < &start[len/sizeof(*c)]; ++c)
		if (!memcmp(c, new, cmplen))
			return c;

	return NULL;
}


void odhcp6c_update_entry_safe(enum odhcp6c_state state, struct odhcp6c_entry *new, uint32_t safe)
{
	size_t len;
	struct odhcp6c_entry *x = odhcp6c_find_entry(state, new);
	struct odhcp6c_entry *start = odhcp6c_get_state(state, &len);
Beispiel #6
0
int main(_unused int argc, char* const argv[])
{
	// Allocate ressources
	const char *pidfile = NULL;
	const char *script = "/usr/sbin/odhcp6c-update";
	ssize_t l;
	uint8_t buf[134];
	char *optpos;
	uint16_t opttype;
	uint16_t optlen;
	enum odhcp6c_ia_mode ia_na_mode = IA_MODE_TRY;
	enum odhcp6c_ia_mode ia_pd_mode = IA_MODE_NONE;
	int ia_pd_iaid_index = 0;
	static struct in6_addr ifid = IN6ADDR_ANY_INIT;
	int sol_timeout = DHCPV6_SOL_MAX_RT;

#ifdef EXT_BFD_PING
	int bfd_interval = 0, bfd_loss = 3;
#endif

	bool help = false, daemonize = false;
	int logopt = LOG_PID;
	int c;
	unsigned int client_options = DHCPV6_CLIENT_FQDN | DHCPV6_ACCEPT_RECONFIGURE;

	while ((c = getopt(argc, argv, "S::N:V:P:FB:c:i:r:Ru:s:kt:m:hedp:fa")) != -1) {
		switch (c) {
		case 'S':
			allow_slaac_only = (optarg) ? atoi(optarg) : -1;
			break;

		case 'N':
			if (!strcmp(optarg, "force")) {
				ia_na_mode = IA_MODE_FORCE;
				allow_slaac_only = -1;
			} else if (!strcmp(optarg, "none")) {
				ia_na_mode = IA_MODE_NONE;
			} else if (!strcmp(optarg, "try")) {
				ia_na_mode = IA_MODE_TRY;
			} else{
				help = true;
			}
			break;

		case 'V':
			l = script_unhexlify(buf, sizeof(buf), optarg);
			if (!l)
				help=true;

			odhcp6c_add_state(STATE_VENDORCLASS, buf, l);

			break;
		case 'P':
			if (ia_pd_mode == IA_MODE_NONE)
				ia_pd_mode = IA_MODE_TRY;

			if (allow_slaac_only >= 0 && allow_slaac_only < 10)
				allow_slaac_only = 10;

			char *iaid_begin;
			int iaid_len = 0;

			int prefix_length = strtoul(optarg, &iaid_begin, 10);

			if (*iaid_begin != '\0' && *iaid_begin != ',' && *iaid_begin != ':') {
				syslog(LOG_ERR, "invalid argument: '%s'", optarg);
				return 1;
			}

			struct odhcp6c_request_prefix prefix = { 0, prefix_length };

			if (*iaid_begin == ',' && (iaid_len = strlen(iaid_begin)) > 1)
				memcpy(&prefix.iaid, iaid_begin + 1, iaid_len > 4 ? 4 : iaid_len);
			else if (*iaid_begin == ':')
				prefix.iaid = htonl((uint32_t)strtoul(&iaid_begin[1], NULL, 16));
			else
				prefix.iaid = htonl(++ia_pd_iaid_index);

			odhcp6c_add_state(STATE_IA_PD_INIT, &prefix, sizeof(prefix));

			break;

		case 'F':
			allow_slaac_only = -1;
			ia_pd_mode = IA_MODE_FORCE;
			break;

#ifdef EXT_BFD_PING
		case 'B':
			bfd_interval = atoi(optarg);
			break;
#endif

		case 'c':
			l = script_unhexlify(&buf[4], sizeof(buf) - 4, optarg);
			if (l > 0) {
				buf[0] = 0;
				buf[1] = DHCPV6_OPT_CLIENTID;
				buf[2] = 0;
				buf[3] = l;
				odhcp6c_add_state(STATE_CLIENT_ID, buf, l + 4);
			} else {
				help = true;
			}
			break;

		case 'i':
			if (inet_pton(AF_INET6, optarg, &ifid) != 1)
				help = true;
			break;

		case 'r':
			optpos = optarg;
			while (optpos[0]) {
				opttype = htons(strtoul(optarg, &optpos, 10));
				if (optpos == optarg)
					break;
				else if (optpos[0])
					optarg = &optpos[1];
				odhcp6c_add_state(STATE_ORO, &opttype, 2);
			}
			break;

		case 'R':
			client_options |= DHCPV6_STRICT_OPTIONS;
			break;

		case 'u':
			optlen = htons(strlen(optarg));
			odhcp6c_add_state(STATE_USERCLASS, &optlen, 2);
			odhcp6c_add_state(STATE_USERCLASS, optarg, strlen(optarg));
			break;

		case 's':
			script = optarg;
			break;

		case 'k':
			release = false;
			break;

		case 't':
			sol_timeout = atoi(optarg);
			break;

		case 'm':
			min_update_interval = atoi(optarg);
			break;

		case 'e':
			logopt |= LOG_PERROR;
			break;

		case 'd':
			daemonize = true;
			break;

		case 'p':
			pidfile = optarg;
			break;

		case 'f':
			client_options &= ~DHCPV6_CLIENT_FQDN;
			break;

		case 'a':
			client_options &= ~DHCPV6_ACCEPT_RECONFIGURE;
			break;

		default:
			help = true;
			break;
		}
	}

	openlog("odhcp6c", logopt, LOG_DAEMON);
	const char *ifname = argv[optind];

	if (help || !ifname)
		return usage();

	signal(SIGIO, sighandler);
	signal(SIGHUP, sighandler);
	signal(SIGINT, sighandler);
	signal(SIGCHLD, sighandler);
	signal(SIGTERM, sighandler);
	signal(SIGUSR1, sighandler);
	signal(SIGUSR2, sighandler);

	if ((urandom_fd = open("/dev/urandom", O_CLOEXEC | O_RDONLY)) < 0 ||
			init_dhcpv6(ifname, client_options, sol_timeout) ||
			ra_init(ifname, &ifid) || script_init(script, ifname)) {
		syslog(LOG_ERR, "failed to initialize: %s", strerror(errno));
		return 3;
	}

	if (daemonize) {
		openlog("odhcp6c", LOG_PID, LOG_DAEMON); // Disable LOG_PERROR
		if (daemon(0, 0)) {
			syslog(LOG_ERR, "Failed to daemonize: %s",
					strerror(errno));
			return 4;
		}

		char pidbuf[128];
		if (!pidfile) {
			snprintf(pidbuf, sizeof(pidbuf),
					"/var/run/odhcp6c.%s.pid", ifname);
			pidfile = pidbuf;
		}

		int fd = open(pidfile, O_WRONLY | O_CREAT, 0644);
		if (fd >= 0) {
			char buf[8];
			int len = snprintf(buf, sizeof(buf), "%i\n", getpid());
			write(fd, buf, len);
			close(fd);
		}
	}

	script_call("started");

	while (!signal_term) { // Main logic
		odhcp6c_clear_state(STATE_SERVER_ID);
		odhcp6c_clear_state(STATE_IA_NA);
		odhcp6c_clear_state(STATE_IA_PD);
		odhcp6c_clear_state(STATE_SNTP_IP);
		odhcp6c_clear_state(STATE_NTP_IP);
		odhcp6c_clear_state(STATE_NTP_FQDN);
		odhcp6c_clear_state(STATE_SIP_IP);
		odhcp6c_clear_state(STATE_SIP_FQDN);
		dhcpv6_set_ia_mode(ia_na_mode, ia_pd_mode);
		bound = false;

		syslog(LOG_NOTICE, "(re)starting transaction on %s", ifname);

		signal_usr1 = signal_usr2 = false;
		int mode = dhcpv6_request(DHCPV6_MSG_SOLICIT);
		odhcp6c_signal_process();

		if (mode < 0)
			continue;

		do {
			int res = dhcpv6_request(mode == DHCPV6_STATELESS ?
					DHCPV6_MSG_INFO_REQ : DHCPV6_MSG_REQUEST);
			bool signalled = odhcp6c_signal_process();

			if (res > 0)
				break;
			else if (signalled) {
				mode = -1;
				break;
			}

			mode = dhcpv6_promote_server_cand();
		} while (mode > DHCPV6_UNKNOWN);

		if (mode < 0)
			continue;

		switch (mode) {
		case DHCPV6_STATELESS:
			bound = true;
			syslog(LOG_NOTICE, "entering stateless-mode on %s", ifname);

			while (!signal_usr2 && !signal_term) {
				signal_usr1 = false;
				script_call("informed");

				int res = dhcpv6_poll_reconfigure();
				odhcp6c_signal_process();

				if (res > 0)
					continue;

				if (signal_usr1) {
					signal_usr1 = false; // Acknowledged
					continue;
				}
				if (signal_usr2 || signal_term)
					break;

				res = dhcpv6_request(DHCPV6_MSG_INFO_REQ);
				odhcp6c_signal_process();
				if (signal_usr1)
					continue;
				else if (res < 0)
					break;
			}
			break;

		case DHCPV6_STATEFUL:
			script_call("bound");
			bound = true;
			syslog(LOG_NOTICE, "entering stateful-mode on %s", ifname);
#ifdef EXT_BFD_PING
			if (bfd_interval > 0)
				bfd_start(ifname, bfd_loss, bfd_interval);
#endif

			while (!signal_usr2 && !signal_term) {
				// Renew Cycle
				// Wait for T1 to expire or until we get a reconfigure
				int res = dhcpv6_poll_reconfigure();
				odhcp6c_signal_process();
				if (res > 0) {
					script_call("updated");
					continue;
				}

				// Handle signal, if necessary
				if (signal_usr1)
					signal_usr1 = false; // Acknowledged
				if (signal_usr2 || signal_term)
					break; // Other signal type

				// Send renew as T1 expired
				res = dhcpv6_request(DHCPV6_MSG_RENEW);
				odhcp6c_signal_process();
				if (res > 0) { // Renew was succesfull
					// Publish updates
					script_call("updated");
					continue; // Renew was successful
				}

				odhcp6c_clear_state(STATE_SERVER_ID); // Remove binding

				size_t ia_pd_len, ia_na_len;
				odhcp6c_get_state(STATE_IA_PD, &ia_pd_len);
				odhcp6c_get_state(STATE_IA_NA, &ia_na_len);

				if (ia_pd_len == 0 && ia_na_len == 0)
					break;

				// If we have IAs, try rebind otherwise restart
				res = dhcpv6_request(DHCPV6_MSG_REBIND);
				odhcp6c_signal_process();

				if (res > 0)
					script_call("rebound");
				else {
#ifdef EXT_BFD_PING
					bfd_stop();
#endif
					break;
				}
			}
			break;

		default:
			break;
		}

		size_t ia_pd_len, ia_na_len, server_id_len;
		odhcp6c_get_state(STATE_IA_PD, &ia_pd_len);
		odhcp6c_get_state(STATE_IA_NA, &ia_na_len);
		odhcp6c_get_state(STATE_SERVER_ID, &server_id_len);

		// Add all prefixes to lost prefixes
		bound = false;
		script_call("unbound");

		if (server_id_len > 0 && (ia_pd_len > 0 || ia_na_len > 0) && release)
			dhcpv6_request(DHCPV6_MSG_RELEASE);

		odhcp6c_clear_state(STATE_IA_NA);
		odhcp6c_clear_state(STATE_IA_PD);
	}

	script_call("stopped");
	return 0;
}
Beispiel #7
0
	return data;
}


void* odhcp6c_get_state(enum odhcp6c_state state, size_t *len)
{
	*len = state_len[state];
	return state_data[state];
}


static struct odhcp6c_entry* odhcp6c_find_entry(enum odhcp6c_state state, const struct odhcp6c_entry *new)
{
	size_t len, cmplen = offsetof(struct odhcp6c_entry, target) + ((new->length + 7) / 8);
	uint8_t *start = odhcp6c_get_state(state, &len);

	for (struct odhcp6c_entry *c = (struct odhcp6c_entry*)start;
			(uint8_t*)c < &start[len] && &c->auxtarget[c->auxlen] <= &start[len];
			c = (struct odhcp6c_entry*)(&c->auxtarget[c->auxlen]))
		if (!memcmp(c, new, cmplen) && !memcmp(c->auxtarget, new->auxtarget, new->auxlen))
			return c;

	return NULL;
}


bool odhcp6c_update_entry(enum odhcp6c_state state, struct odhcp6c_entry *new,
		uint32_t safe, bool filterexcess)
{
	size_t len;
Beispiel #8
0
static void bfd_send(int signal __attribute__((unused)))
{
	struct {
		struct ip6_hdr ip6;
		struct icmp6_hdr icmp6;
	} ping;
	memset(&ping, 0, sizeof(ping));

	ping.ip6.ip6_vfc = 6 << 4;
	ping.ip6.ip6_plen = htons(8);
	ping.ip6.ip6_nxt = IPPROTO_ICMPV6;
	ping.ip6.ip6_hlim = 255;

	ping.icmp6.icmp6_type = ICMP6_ECHO_REQUEST;
	ping.icmp6.icmp6_data32[0] = htonl(0xbfd0bfd);

	size_t pdlen, rtlen;
	struct odhcp6c_entry *pd = odhcp6c_get_state(STATE_IA_PD, &pdlen), *cpd = NULL;
	struct odhcp6c_entry *rt = odhcp6c_get_state(STATE_RA_ROUTE, &rtlen), *crt = NULL;
	bool crt_found = false;

	alarm(bfd_interval);

	if (bfd_armed) {
		if (++bfd_failed > bfd_limit) {
			raise(SIGUSR2);
			return;
		}
	}

	// Detect PD-Prefix
	for (size_t i = 0; i < pdlen / sizeof(*pd); ++i)
		if (!cpd || ((cpd->target.s6_addr[0] & 7) == 0xfc) > ((pd[i].target.s6_addr[0] & 7) == 0xfc)
				|| cpd->preferred < pd[i].preferred)
			cpd = &pd[i];

	// Detect default router
	for (size_t i = 0; i < rtlen / sizeof(*rt); ++i)
		if (IN6_IS_ADDR_UNSPECIFIED(&rt[i].target) && (!crt || crt->priority > rt[i].priority))
			crt = &rt[i];

	struct sockaddr_ll dest = {
		.sll_family = AF_PACKET,
		.sll_protocol = htons(ETH_P_IPV6),
		.sll_ifindex = if_index,
		.sll_halen = ETH_ALEN,
	};

	if (crt) {
		struct {
			struct nlmsghdr hdr;
			struct ndmsg ndm;
		} req = {
			.hdr = {sizeof(req), RTM_GETNEIGH, NLM_F_REQUEST | NLM_F_DUMP, 1, 0},
			.ndm = {.ndm_family = AF_INET6, .ndm_ifindex = if_index}
		};
		send(rtnl, &req, sizeof(req), 0);

		uint8_t buf[8192];
		struct nlmsghdr *nhm;
		do {
			ssize_t read = recv(rtnl, buf, sizeof(buf), 0);
			nhm = (struct nlmsghdr*)buf;
			if ((read < 0 && errno == EINTR) || !NLMSG_OK(nhm, (size_t)read))
				continue;
			else if (read < 0)
				break;

			for (; read > 0 && NLMSG_OK(nhm, (size_t)read); nhm = NLMSG_NEXT(nhm, read)) {
				ssize_t attrlen = NLMSG_PAYLOAD(nhm, sizeof(struct ndmsg));
				if (nhm->nlmsg_type != RTM_NEWNEIGH || attrlen <= 0) {
					nhm = NULL;
					break;
				}

				// Already have our MAC
				if (crt_found)
					continue;

				struct ndmsg *ndm = NLMSG_DATA(nhm);
				for (struct rtattr *rta = (struct rtattr*)&ndm[1];
						attrlen > 0 && RTA_OK(rta, (size_t)attrlen);
						rta = RTA_NEXT(rta, attrlen)) {
					if (rta->rta_type == NDA_DST) {
						crt_found = IN6_ARE_ADDR_EQUAL(RTA_DATA(rta), &crt->router);
					} else if (rta->rta_type == NDA_LLADDR) {
						memcpy(dest.sll_addr, RTA_DATA(rta), ETH_ALEN);
					}
				}
			}
		} while (nhm);
	}