Exemplo n.º 1
0
int
kernel_addresses(char *ifname, int ifindex, int ll,
                 struct kernel_route *routes, int maxroutes)
{
    struct ifaddrs *ifa, *ifap;
    int rc, i;

    rc = getifaddrs(&ifa);
    if(rc < 0)
        return -1;

    ifap = ifa;
    i = 0;

    while(ifap && i < maxroutes) {
        if((ifname != NULL && strcmp(ifap->ifa_name, ifname) != 0))
            goto next;
        if(ifap->ifa_addr->sa_family == AF_INET6) {
            struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)ifap->ifa_addr;
            if(!!ll != !!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
                goto next;
            memcpy(routes[i].prefix, &sin6->sin6_addr, 16);
            if(ll)
                /* This a perfect example of counter-productive optimisation :
                   KAME encodes interface index onto bytes 2 and 3, so we have to
                   reset those bytes to 0 before passing them to babeld. */
                memset(routes[i].prefix + 2, 0, 2);
            routes[i].plen = 128;
            routes[i].metric = 0;
            routes[i].ifindex = ifindex;
            routes[i].proto = RTPROT_BABEL_LOCAL;
            memset(routes[i].gw, 0, 16);
            i++;
        } else if(ifap->ifa_addr->sa_family == AF_INET) {
            struct sockaddr_in *sin = (struct sockaddr_in*)ifap->ifa_addr;
            if(ll)
                goto next;
#if defined(IN_LINKLOCAL)
            if(IN_LINKLOCAL(htonl(sin->sin_addr.s_addr)))
                goto next;
#endif
            memcpy(routes[i].prefix, v4prefix, 12);
            memcpy(routes[i].prefix + 12, &sin->sin_addr, 4);
            routes[i].plen = 128;
            routes[i].metric = 0;
            routes[i].ifindex = ifindex;
            routes[i].proto = RTPROT_BABEL_LOCAL;
            memset(routes[i].gw, 0, 16);
            i++;
        }
 next:
        ifap = ifap->ifa_next;
    }

    freeifaddrs(ifa);
    return i;
}
Exemplo n.º 2
0
/* Check if valid address */
static int valid_addr(struct in_addr *ina)
{
	in_addr_t addr;

	addr = ntohl(ina->s_addr);
	if (IN_ZERONET(addr) || IN_LOOPBACK(addr) || IN_LINKLOCAL(addr))
		return 0;

	return 1;
}
Exemplo n.º 3
0
void
ipv4ll_start(void *arg)
{
	struct interface *ifp = arg;
	struct dhcp_state *state = D_STATE(ifp);
	uint32_t addr;

	eloop_timeout_delete(ifp->ctx->eloop, NULL, ifp);
	state->probes = 0;
	state->claims = 0;
	if (state->addr.s_addr) {
		state->conflicts = 0;
		if (IN_LINKLOCAL(htonl(state->addr.s_addr))) {
			arp_announce(ifp);
			return;
		}
	}

	if (state->offer == NULL)
		addr = 0;
	else {
		addr = state->offer->yiaddr;
		free(state->offer);
	}
	/* We maybe rebooting an IPv4LL address. */
	if (!IN_LINKLOCAL(htonl(addr))) {
		syslog(LOG_INFO, "%s: probing for an IPv4LL address",
		    ifp->name);
		addr = 0;
	}
	if (addr == 0)
		state->offer = ipv4ll_find_lease(addr);
	else
		state->offer = ipv4ll_make_lease(addr);
	if (state->offer == NULL)
		syslog(LOG_ERR, "%s: %m", __func__);
	else {
		state->lease.frominfo = 0;
		arp_probe(ifp);
	}
}
Exemplo n.º 4
0
static struct dhcp_message *
ipv4ll_find_lease(uint32_t old_addr)
{
	uint32_t addr;

	for (;;) {
		addr = htonl(LINKLOCAL_ADDR |
		    (((uint32_t)abs((int)arc4random())
			% 0xFD00) + 0x0100));
		if (addr != old_addr &&
		    IN_LINKLOCAL(ntohl(addr)))
			break;
	}
	return ipv4ll_make_lease(addr);
}
Exemplo n.º 5
0
static
Boolean _CFNetDiagnosticIsLinkLocal (CFStringRef s)
{
	char buffer[16];
	Boolean converted;
	Boolean retval = 0;
	uint32_t addr;

	converted = CFStringGetCString(s, buffer, 16, kCFStringEncodingASCII);
	if(converted) {
		addr = inet_addr(buffer);
		if(addr == INADDR_NONE) {
			retval = 0;
		} else {
			retval = IN_LINKLOCAL(ntohl(addr));
		} 
	}

	 return retval;
}
Exemplo n.º 6
0
/*
	Get the IP address from interface
*/
static RC_TYPE do_ip_check_interface(DYN_DNS_CLIENT *p_self)
{
	struct ifreq ifr;
	in_addr_t new_ip;
	char *new_ip_str;
	int i;

	if (p_self == NULL)
	{
		return RC_INVALID_POINTER;
	}

	if (p_self->check_interface)
	{
		logit(LOG_INFO, MODULE_TAG "Checking for IP# change, querying interface %s",
		      p_self->check_interface);

		int sd = socket(PF_INET, SOCK_DGRAM, 0);

		if (sd < 0)
		{
			int code = os_get_socket_error();

			logit(LOG_WARNING, MODULE_TAG "Failed opening network socket: %s", strerror(code));
			return RC_IP_OS_SOCKET_INIT_FAILED;
		}

		memset(&ifr, 0, sizeof(struct ifreq));
		ifr.ifr_addr.sa_family = AF_INET;
		snprintf(ifr.ifr_name, IFNAMSIZ, p_self->check_interface);
		if (ioctl(sd, SIOCGIFADDR, &ifr) != -1)
		{	
			new_ip = ntohl(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr);
			new_ip_str = inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr);
		}
		else
		{
			int code = os_get_socket_error();

			logit(LOG_ERR, MODULE_TAG "Failed reading IP address of interface %s: %s",
			      p_self->check_interface, strerror(code));
			return RC_ERROR;
		}
		close(sd);
	}
	else
	{
		return RC_ERROR;
	}

	if (IN_ZERONET(new_ip) ||
	    IN_LOOPBACK(new_ip) ||
	    IN_LINKLOCAL(new_ip) ||
	    IN_MULTICAST(new_ip) ||
	    IN_EXPERIMENTAL(new_ip))
	{
		logit(LOG_WARNING, MODULE_TAG "Interface %s has invalid IP# %s",
		      p_self->check_interface, new_ip_str);
		return RC_ERROR;
	}

	int anychange = 0;

	for (i = 0; i < p_self->info_count; i++)
	{
		DYNDNS_INFO_TYPE *info = &p_self->info[i];

		info->my_ip_has_changed = strcmp(info->my_ip_address.name, new_ip_str) != 0;
		if (info->my_ip_has_changed)
		{
			anychange++;
			strcpy(info->my_ip_address.name, new_ip_str);
		}
	}

	if (!anychange)
	{
		logit(LOG_INFO, MODULE_TAG "No IP# change detected, still at %s", new_ip_str);
	}

	return RC_OK;
}
Exemplo n.º 7
0
ssize_t
make_message(struct dhcp_message **message,
	     const struct interface *iface, const struct dhcp_lease *lease,
	     uint32_t xid, uint8_t type, const struct options *options)
{
	struct dhcp_message *dhcp;
	uint8_t *m, *lp, *p;
	uint8_t *n_params = NULL;
	time_t up = uptime() - iface->start_uptime;
	uint32_t ul;
	uint16_t sz;
	const struct dhcp_opt *opt;
	size_t len;
	const char *hp;

	dhcp = xzalloc(sizeof (*dhcp));
	m = (uint8_t *)dhcp;
	p = dhcp->options;

	if ((type == DHCP_INFORM ||
	     type == DHCP_RELEASE ||
	     type == DHCP_REQUEST) &&
	    !IN_LINKLOCAL(ntohl(iface->addr.s_addr)))
	{
		dhcp->ciaddr = iface->addr.s_addr;
		/* Just incase we haven't actually configured the address yet */
		if (type == DHCP_INFORM && iface->addr.s_addr == 0)
			dhcp->ciaddr = lease->addr.s_addr;
		/* Zero the address if we're currently on a different subnet */
		if (type == DHCP_REQUEST &&
		    iface->net.s_addr != lease->net.s_addr)
			dhcp->ciaddr = 0;
	}

	dhcp->op = DHCP_BOOTREQUEST;
	dhcp->hwtype = iface->family;
	switch (iface->family) {
	case ARPHRD_ETHER:
	case ARPHRD_IEEE802:
		dhcp->hwlen = ETHER_ADDR_LEN;
		memcpy(&dhcp->chaddr, &iface->hwaddr, ETHER_ADDR_LEN);
		break;
	case ARPHRD_IEEE1394:
	case ARPHRD_INFINIBAND:
		dhcp->hwlen = 0;
		if (dhcp->ciaddr == 0 &&
		    type != DHCP_DECLINE && type != DHCP_RELEASE)
			dhcp->flags = htons(BROADCAST_FLAG);
		break;
	}

	if (type != DHCP_DECLINE && type != DHCP_RELEASE) {
		if (up < 0 || up > (time_t)UINT16_MAX)
			dhcp->secs = htons((uint16_t)UINT16_MAX);
		else
			dhcp->secs = htons(up);
	}
	dhcp->xid = xid;
	dhcp->cookie = htonl(MAGIC_COOKIE);

	*p++ = DHO_MESSAGETYPE; 
	*p++ = 1;
	*p++ = type;

	if (iface->clientid) {
		*p++ = DHO_CLIENTID;
		memcpy(p, iface->clientid, iface->clientid[0] + 1);
		p += iface->clientid[0] + 1;
	}

	if (lease->addr.s_addr && !IN_LINKLOCAL(htonl(lease->addr.s_addr))) {
		if (type == DHCP_DECLINE ||
		    type == DHCP_DISCOVER ||
		    (type == DHCP_REQUEST &&
		     lease->addr.s_addr != iface->addr.s_addr))
		{
			PUTADDR(DHO_IPADDRESS, lease->addr);
			if (lease->server.s_addr)
				PUTADDR(DHO_SERVERID, lease->server);
		}
	}

	if (type == DHCP_DECLINE) {
		*p++ = DHO_MESSAGE;
		len = strlen(DAD);
		*p++ = len;
		memcpy(p, DAD, len);
		p += len;
	}

	if (type == DHCP_RELEASE) {
		if (lease->server.s_addr)
			PUTADDR(DHO_SERVERID, lease->server);
	}

	if (type == DHCP_DISCOVER ||
	    type == DHCP_INFORM ||
	    type == DHCP_REQUEST)
	{
		*p++ = DHO_MAXMESSAGESIZE;
		*p++ = 2;
		sz = get_mtu(iface->name);
		if (sz < MTU_MIN) {
			if (set_mtu(iface->name, MTU_MIN) == 0)
				sz = MTU_MIN;
		}
		sz = htons(sz);
		memcpy(p, &sz, 2);
		p += 2;

		if (options->userclass[0]) {
			*p++ = DHO_USERCLASS;
			memcpy(p, options->userclass, options->userclass[0] + 1);
			p += options->userclass[0] + 1;
		}

		if (options->vendorclassid[0]) {
			*p++ = DHO_VENDORCLASSID;
			memcpy(p, options->vendorclassid,
			       options->vendorclassid[0] + 1);
			p += options->vendorclassid[0] + 1;
		}

		if (type != DHCP_INFORM) {
			if (options->leasetime != 0) {
				*p++ = DHO_LEASETIME;
				*p++ = 4;
				ul = htonl(options->leasetime);
				memcpy(p, &ul, 4);
				p += 4;
			}
		}

		/* Regardless of RFC2132, we should always send a hostname
		 * upto the first dot (the short hostname) as otherwise
		 * confuses some DHCP servers when updating DNS.
		 * The FQDN option should be used if a FQDN is required. */
		if (options->hostname[0]) {
			*p++ = DHO_HOSTNAME;
			hp = strchr(options->hostname, '.');
			if (hp)
				len = hp - options->hostname;
			else
				len = strlen(options->hostname);
			*p++ = len;
			memcpy(p, options->hostname, len);
			p += len;
		}
		if (options->fqdn != FQDN_DISABLE) {
			/* IETF DHC-FQDN option (81), RFC4702 */
			*p++ = DHO_FQDN;
			lp = p;
			*p++ = 3;
			/*
			 * Flags: 0000NEOS
			 * S: 1 => Client requests Server to update
			 *         a RR in DNS as well as PTR
			 * O: 1 => Server indicates to client that
			 *         DNS has been updated
			 * E: 1 => Name data is DNS format
			 * N: 1 => Client requests Server to not
			 *         update DNS
			 */
			*p++ = (options->fqdn & 0x09) | 0x04;
			*p++ = 0; /* from server for PTR RR */
			*p++ = 0; /* from server for A RR if S=1 */
			ul = encode_rfc1035(options->hostname, p);
			*lp += ul;
			p += ul;
		}

		/* vendor is already encoded correctly, so just add it */
		if (options->vendor[0]) {
			*p++ = DHO_VENDOR;
			memcpy(p, options->vendor, options->vendor[0] + 1);
			p += options->vendor[0] + 1;
		}

		*p++ = DHO_PARAMETERREQUESTLIST;
		n_params = p;
		*p++ = 0;
		for (opt = dhcp_opts; opt->option; opt++) {
			if (!(opt->type & REQUEST || 
			      has_option_mask(options->requestmask, opt->option)))
				continue;
			switch (opt->option) {
			case DHO_RENEWALTIME:	/* FALLTHROUGH */
			case DHO_REBINDTIME:
				if (type == DHCP_INFORM)
					continue;
				break;
			}
			*p++ = opt->option;
		}
		*n_params = p - n_params - 1;
	}
	*p++ = DHO_END;

#ifdef BOOTP_MESSAGE_LENTH_MIN
	/* Some crappy DHCP servers think they have to obey the BOOTP minimum
	 * message length.
	 * They are wrong, but we should still cater for them. */
	while (p - m < BOOTP_MESSAGE_LENTH_MIN)
		*p++ = DHO_PAD;
#endif

	*message = dhcp;
	return p - m;
}
Exemplo n.º 8
0
int main(int argc, char **argv)
{
	options_t *options;
	int userclasses = 0;
	int opt;
	int option_index = 0;
	char *prefix;
	pid_t pid;
	int debug = 0;
	int i;
	int pidfd = -1;
	int sig = 0;

	/* Close any un-needed fd's */
	for (i = getdtablesize() - 1; i >= 3; --i)
		close (i);

	openlog (PACKAGE, LOG_PID, LOG_LOCAL0);

	options = xmalloc (sizeof (options_t));
	memset (options, 0, sizeof (options_t));
	options->script = (char *) DEFAULT_SCRIPT;
	snprintf (options->classid, CLASS_ID_MAX_LEN, "%s %s", PACKAGE, VERSION);
	options->classid_len = strlen (options->classid);

	options->doarp = true;
	options->dodns = true;
	options->domtu = true;
	options->donis = true;
	options->dontp = true;
	options->dogateway = true;
	options->daemonise = true;
	options->doinform = false;
	options->doipv4ll = true;
	options->timeout = DEFAULT_TIMEOUT;

	gethostname (options->hostname, sizeof (options->hostname));
	if (strcmp (options->hostname, "(none)") == 0 ||
		strcmp (options->hostname, "localhost") == 0)
		memset (options->hostname, 0, sizeof (options->hostname));

	/* Don't set any optional arguments here so we retain POSIX
	 * compatibility with getopt */
	while ((opt = getopt_long(argc, argv, EXTRA_OPTS
							  "c:dh:i:kl:m:npr:s:t:u:xAEF:GHI:LMNRTY",
							  longopts, &option_index)) != -1)
	{
		switch (opt) {
			case 0:
				if (longopts[option_index].flag)
					break;
				logger (LOG_ERR, "option `%s' should set a flag",
						longopts[option_index].name);
				exit (EXIT_FAILURE);
				break;
			case 'c':
				options->script = optarg;
				break;
			case 'd':
				debug++;
				switch (debug) {
					case 1:
						setloglevel (LOG_DEBUG);
						break;
					case 2:
						options->daemonise = false;
						break;
				}
				break;
#ifdef THERE_IS_NO_FORK
			case 'f':
				options->daemonised = true;
				close_fds ();
				break;
			case 'g':
				dhcpcd_skiproutes = xstrdup (optarg);
				break;
#endif
			case 'h':
				if (! optarg)
					memset (options->hostname, 0, sizeof (options->hostname));
				else if (strlen (optarg) > MAXHOSTNAMELEN) {
					logger (LOG_ERR, "`%s' too long for HostName string, max is %d",
							optarg, MAXHOSTNAMELEN);
					exit (EXIT_FAILURE);
				} else
					strlcpy (options->hostname, optarg, sizeof (options->hostname));
				break;
			case 'i':
				if (! optarg) {
					memset (options->classid, 0, sizeof (options->classid));
					options->classid_len = 0;
				} else if (strlen (optarg) > CLASS_ID_MAX_LEN) {
					logger (LOG_ERR, "`%s' too long for ClassID string, max is %d",
							optarg, CLASS_ID_MAX_LEN);
					exit (EXIT_FAILURE);
				} else
					options->classid_len = strlcpy (options->classid, optarg,
												   sizeof (options->classid));
				break;
			case 'k':
				sig = SIGHUP;
				break;
			case 'l':
				STRINGINT (optarg, options->leasetime);
				if (options->leasetime <= 0) {
					logger (LOG_ERR, "leasetime must be a positive value");
					exit (EXIT_FAILURE);
				}
				break;
			case 'm':
				STRINGINT (optarg, options->metric);
				break;
			case 'n':
				sig = SIGALRM;
				break;
			case 'p':
				options->persistent = true;
				break;
			case 's':
				options->doinform = true;
				options->doarp = false;
				if (! optarg || strlen (optarg) == 0) {
					options->request_address.s_addr = 0;
					break;
				} else {
					char *slash = strchr (optarg, '/');
					if (slash) {
						int cidr;
						/* nullify the slash, so the -r option can read the
						 * address */
						*slash++ = '\0';
						if (sscanf (slash, "%d", &cidr) != 1 ||
							inet_cidrtoaddr (cidr, &options->request_netmask) != 0) {
							logger (LOG_ERR, "`%s' is not a valid CIDR", slash);
							exit (EXIT_FAILURE);
						}
					}
					/* fall through */
				}
			case 'r':
				if (! options->doinform)
					options->dorequest = true;
				if (strlen (optarg) > 0 &&
					! inet_aton (optarg, &options->request_address))
				{ 
					logger (LOG_ERR, "`%s' is not a valid IP address", optarg);
					exit (EXIT_FAILURE);
				}
				break;
			case 't':
				STRINGINT (optarg, options->timeout);
				if (options->timeout < 0) {
					logger (LOG_ERR, "timeout must be a positive value");
					exit (EXIT_FAILURE);
				}
				break;
			case 'u':
				{
					int offset = 0;
					for (i = 0; i < userclasses; i++)
						offset += (int) options->userclass[offset] + 1;
					if (offset + 1 + strlen (optarg) > USERCLASS_MAX_LEN) {
						logger (LOG_ERR, "userclass overrun, max is %d",
								USERCLASS_MAX_LEN);
						exit (EXIT_FAILURE);
					}
					userclasses++;
					memcpy (options->userclass + offset + 1 , optarg, strlen (optarg));
					options->userclass[offset] = strlen (optarg);
					options->userclass_len += (strlen (optarg)) + 1;
				}
				break;
			case 'x':
				sig = SIGTERM;
				break;
			case 'A':
#ifndef ENABLE_ARP
				logger (LOG_ERR, "arp support not compiled into dhcpcd");
				exit (EXIT_FAILURE);
#endif
				options->doarp = false;
				break;
			case 'E':
#ifndef ENABLE_INFO
				logger (LOG_ERR, "info support not compiled into dhcpcd");
				exit (EXIT_FAILURE);
#endif
				options->dolastlease = true;
				break;
			case 'F':
				if (strncmp (optarg, "none", strlen (optarg)) == 0)
					options->fqdn = FQDN_NONE;
				else if (strncmp (optarg, "ptr", strlen (optarg)) == 0)
					options->fqdn = FQDN_PTR;
				else if (strncmp (optarg, "both", strlen (optarg)) == 0)
					options->fqdn = FQDN_BOTH;
				else {
					logger (LOG_ERR, "invalid value `%s' for FQDN", optarg);
					exit (EXIT_FAILURE);
				}
				break;
			case 'G':
				options->dogateway = false;
				break;
			case 'H':
				options->dohostname++;
				break;
			case 'I':
				if (optarg) {
					if (strlen (optarg) > CLIENT_ID_MAX_LEN) {
						logger (LOG_ERR, "`%s' is too long for ClientID, max is %d",
								optarg, CLIENT_ID_MAX_LEN);
						exit (EXIT_FAILURE);
					}
					options->clientid_len = strlcpy (options->clientid, optarg,
													sizeof (options->clientid));
					/* empty string disabled duid */
					if (options->clientid_len == 0)
						options->clientid_len = -1;
				} else {
					memset (options->clientid, 0, sizeof (options->clientid));
					options->clientid_len = -1;
				}
				break;
			case 'L':
				options->doipv4ll = false;
				break;
			case 'M':
				options->domtu = false;
				break;
			case 'N':
				options->dontp = false;
				break;
			case 'R':
				options->dodns = false;
				break;
			case 'T':
#ifndef ENABLE_INFO
				logger (LOG_ERR, "info support not compiled into dhcpcd");
				exit (EXIT_FAILURE);
#endif
				options->test = true;
				options->persistent = true;
				break;
			case 'Y':
				options->donis = false;
				break;
			case '?':
				usage ();
				exit (EXIT_FAILURE);
			default:
				usage ();
				exit (EXIT_FAILURE);
		}
	}
	if (doversion) {
		printf (""PACKAGE" "VERSION"\n");
		printf ("Compile time options:"
#ifdef ENABLE_ARP
				" ARP"
#endif
#ifdef ENABLE_DUID
				" DUID"
#endif
#ifdef ENABLE_INFO
				" INFO"
#endif
#ifdef ENABLE_INFO_COMPAT
				" INFO_COMPAT"
#endif
#ifdef ENABLE_IPV4LL
				" IPV4LL"
#endif
#ifdef ENABLE_NIS
				" NIS"
#endif
#ifdef ENABLE_NTP
				" NTP"
#endif
#ifdef THERE_IS_NO_FORK
				" THERE_IS_NO_FORK"
#endif
				"\n");
	}

	if (dohelp)
		usage ();

#ifdef THERE_IS_NO_FORK
	dhcpcd_argv = argv;
	dhcpcd_argc = argc;

	/* We need the full path to the dhcpcd */
	if (*argv[0] == '/')
		strlcpy (dhcpcd, argv[0], sizeof (dhcpcd));
	else {
		char pwd[PATH_MAX];
		if (! getcwd (pwd, PATH_MAX)) {
			logger (LOG_ERR, "getcwd: %s", strerror (errno));
			exit (EXIT_FAILURE);
		}
		snprintf (dhcpcd, sizeof (dhcpcd), "%s/%s", pwd, argv[0]);
	}

#endif

	if (optind < argc) {
		if (strlen (argv[optind]) > IF_NAMESIZE) {
			logger (LOG_ERR, "`%s' is too long for an interface name (max=%d)",
					argv[optind], IF_NAMESIZE);
			exit (EXIT_FAILURE);
		}
		strlcpy (options->interface, argv[optind],
				 sizeof (options->interface));
	} else {
		/* If only version was requested then exit now */
		if (doversion || dohelp)
			exit (EXIT_SUCCESS);

		logger (LOG_ERR, "no interface specified");
		exit (EXIT_FAILURE);
	}

	if (strchr (options->hostname, '.')) {
		if (options->fqdn == FQDN_DISABLE)
			options->fqdn = FQDN_BOTH;
	} else
		options->fqdn = FQDN_DISABLE;

	if (options->request_address.s_addr == 0 && options->doinform) {
		if ((options->request_address.s_addr = get_address (options->interface)) != 0)
			options->keep_address = true;
	}

	if (IN_LINKLOCAL (options->request_address.s_addr)) {
		logger (LOG_ERR, "you are not allowed to request a link local address");
		exit (EXIT_FAILURE);
	}

	if (geteuid ()) {
		logger (LOG_ERR, "you need to be root to run "PACKAGE);
		exit (EXIT_FAILURE);
	}

	prefix = xmalloc (sizeof (char) * (IF_NAMESIZE + 3));
	snprintf (prefix, IF_NAMESIZE, "%s: ", options->interface);
	setlogprefix (prefix);
	snprintf (options->pidfile, sizeof (options->pidfile), PIDFILE,
			  options->interface);
	free (prefix);

	chdir ("/");
	umask (022);
	
	if (mkdir (CONFIGDIR, S_IRUSR |S_IWUSR |S_IXUSR | S_IRGRP | S_IXGRP
			   | S_IROTH | S_IXOTH) && errno != EEXIST )
	{
		logger (LOG_ERR, "mkdir(\"%s\",0): %s\n", CONFIGDIR, strerror (errno));
		exit (EXIT_FAILURE);
	}

	if (mkdir (ETCDIR, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP
			   | S_IROTH | S_IXOTH) && errno != EEXIST )
	{
		logger (LOG_ERR, "mkdir(\"%s\",0): %s\n", ETCDIR, strerror (errno));
		exit (EXIT_FAILURE);
	}

	if (options->test) {
		if (options->dorequest || options->doinform) {
			logger (LOG_ERR, "cannot test with --inform or --request");
			exit (EXIT_FAILURE);
		}

		if (options->dolastlease) {
			logger (LOG_ERR, "cannot test with --lastlease");
			exit (EXIT_FAILURE);
		}

		if (sig != 0) {
			logger (LOG_ERR, "cannot test with --release or --renew");
			exit (EXIT_FAILURE);
		}
	}

	if (sig != 0) {
		int killed = -1;
		pid = read_pid (options->pidfile);
		if (pid != 0)
			logger (LOG_INFO, "sending signal %d to pid %d", sig, pid);

		if (! pid || (killed = kill (pid, sig)))
			logger (sig == SIGALRM ? LOG_INFO : LOG_ERR, ""PACKAGE" not running");

		if (pid != 0 && (sig != SIGALRM || killed != 0))
			unlink (options->pidfile);

		if (killed == 0)
			exit (EXIT_SUCCESS);

		if (sig != SIGALRM)
			exit (EXIT_FAILURE);
	}

	if (! options->test && ! options->daemonised) {
		if ((pid = read_pid (options->pidfile)) > 0 && kill (pid, 0) == 0) {
			logger (LOG_ERR, ""PACKAGE" already running on pid %d (%s)",
					pid, options->pidfile);
			exit (EXIT_FAILURE);
		}

		pidfd = open (options->pidfile, O_WRONLY | O_CREAT | O_NONBLOCK, 0660);
		if (pidfd == -1) {
			logger (LOG_ERR, "open `%s': %s", options->pidfile, strerror (errno));
			exit (EXIT_FAILURE);
		}

		/* Lock the file so that only one instance of dhcpcd runs on an interface */
		if (flock (pidfd, LOCK_EX | LOCK_NB) == -1) {
			logger (LOG_ERR, "flock `%s': %s", options->pidfile, strerror (errno));
			exit (EXIT_FAILURE);
		}

		/* dhcpcd.sh should not interhit this fd */
		if ((i = fcntl (pidfd, F_GETFD, 0)) == -1 ||
			fcntl (pidfd, F_SETFD, i | FD_CLOEXEC) == -1)
			logger (LOG_ERR, "fcntl: %s", strerror (errno));

		writepid (pidfd, getpid ());
		logger (LOG_INFO, PACKAGE " " VERSION " starting");
	}

	/* Seed random */
	srandomdev ();

	i = EXIT_FAILURE;
	if (dhcp_run (options, &pidfd) == 0)
		i = EXIT_SUCCESS;

	/* If we didn't daemonise then we need to punt the pidfile now */
	if (pidfd > -1) {
		close (pidfd);
		unlink (options->pidfile);
	}

	free (options);

#ifdef THERE_IS_NO_FORK
	/* There may have been an error before the dhcp_run function
	 * clears this, so just do it here to be safe */
	free (dhcpcd_skiproutes);
#endif

	logger (LOG_INFO, "exiting");
	
	exit (i);
}
Exemplo n.º 9
0
size_t send_message (const interface_t *iface, const dhcp_t *dhcp,
					 unsigned long xid, char type,
					 const options_t *options)
{
	struct udp_dhcp_packet *packet;
	dhcpmessage_t *message;
	unsigned char *m;
	unsigned char *p;
	unsigned char *n_params = NULL;
	unsigned long l;
	struct in_addr from;
	struct in_addr to;
	time_t up = uptime() - iface->start_uptime;
	uint32_t ul;
	uint16_t sz;
	unsigned int message_length;
	size_t retval;

	if (!iface || !options || !dhcp)
		return -1;

	memset (&from, 0, sizeof (from));
	memset (&to, 0, sizeof (to));

	if (type == DHCP_RELEASE)
		to.s_addr = dhcp->serveraddress.s_addr;

	message = xmalloc (sizeof (dhcpmessage_t));
	memset (message, 0, sizeof (dhcpmessage_t));
	m = (unsigned char *) message;
	p = (unsigned char *) &message->options;

	if ((type == DHCP_INFORM ||
		type == DHCP_RELEASE ||
		type == DHCP_REQUEST) &&
		! IN_LINKLOCAL (iface->previous_address.s_addr))
	{
		message->ciaddr = iface->previous_address.s_addr;
		from.s_addr = iface->previous_address.s_addr;

		/* Just incase we haven't actually configured the address yet */
		if (type == DHCP_INFORM && iface->previous_address.s_addr == 0)
			message->ciaddr = dhcp->address.s_addr;
		
		/* Zero the address if we're currently on a different subnet */
		if (type == DHCP_REQUEST &&
			iface->previous_netmask.s_addr != dhcp->netmask.s_addr)
			message->ciaddr = from.s_addr = 0;
	}

	message->op = DHCP_BOOTREQUEST;
	message->hwtype = iface->family;
	switch (iface->family) {
		case ARPHRD_ETHER:
		case ARPHRD_IEEE802:
			message->hwlen = ETHER_ADDR_LEN;
			memcpy (&message->chaddr, &iface->hwaddr, ETHER_ADDR_LEN);
			break;
		case ARPHRD_IEEE1394:
		case ARPHRD_INFINIBAND:
			if (message->ciaddr == 0)
				message->flags = (int16_t) htons (BROADCAST_FLAG);
			message->hwlen = 0;
			break;
		default:
			logger (LOG_ERR, "dhcp: unknown hardware type %d", iface->family);
	}

	if (up < 0 || up > UINT16_MAX)
		message->secs = htons ((short) UINT16_MAX);
	else
		message->secs = htons (up);
	message->xid = xid;
	message->cookie = htonl (MAGIC_COOKIE);

	*p++ = DHCP_MESSAGETYPE; 
	*p++ = 1;
	*p++ = type;

	if (type == DHCP_REQUEST) {
		*p++ = DHCP_MAXMESSAGESIZE;
		*p++ = 2;
		sz = get_mtu (iface->name);
		if (sz < MTU_MIN) {
			if (set_mtu (iface->name, MTU_MIN) == 0)
				sz = MTU_MIN;
		}
		sz = htons (sz);
		memcpy (p, &sz, 2);
		p += 2;
	}

	if (type != DHCP_INFORM) {
#define PUTADDR(_type, _val) \
		{ \
			*p++ = _type; \
			*p++ = 4; \
			memcpy (p, &_val.s_addr, 4); \
			p += 4; \
		}
		if (IN_LINKLOCAL (dhcp->address.s_addr))
			logger (LOG_ERR, "cannot request a link local address");
		else {
			if (dhcp->address.s_addr != iface->previous_address.s_addr &&
				type != DHCP_RELEASE)
				PUTADDR (DHCP_ADDRESS, dhcp->address);

			if (dhcp->serveraddress.s_addr != 0 && dhcp->address.s_addr !=0 &&
				(iface->previous_address.s_addr == 0 || type == DHCP_RELEASE))
				PUTADDR (DHCP_SERVERIDENTIFIER, dhcp->serveraddress);
		}
#undef PUTADDR
	}

	if (type == DHCP_REQUEST || type == DHCP_DISCOVER) {
		if (options->leasetime != 0) {
			*p++ = DHCP_LEASETIME;
			*p++ = 4;
			ul = htonl (options->leasetime);
			memcpy (p, &ul, 4);
			p += 4;
		}
	}

	if (type == DHCP_DISCOVER || type == DHCP_INFORM || type == DHCP_REQUEST) {
		*p++ = DHCP_PARAMETERREQUESTLIST;
		n_params = p;
		*p++ = 0;

		/* Only request DNSSERVER in discover to keep the packets small.
		   RFC2131 Section 3.5 states that the REQUEST must include the list
		   from the DISCOVER message, so I think we can safely do this. */

		if (type == DHCP_DISCOVER && ! options->test)
			*p++ = DHCP_DNSSERVER;
		else {
			if (type != DHCP_INFORM) {
				*p++ = DHCP_RENEWALTIME;
				*p++ = DHCP_REBINDTIME;
			}
			*p++ = DHCP_NETMASK;
			*p++ = DHCP_BROADCAST;
			*p++ = DHCP_CSR;
			/* RFC 3442 states classless static routes should be before routers
			 * and static routes as classless static routes override them both */
			*p++ = DHCP_STATICROUTE;
			*p++ = DHCP_ROUTERS;
			*p++ = DHCP_HOSTNAME;
			*p++ = DHCP_DNSSEARCH;
			*p++ = DHCP_DNSDOMAIN;
			*p++ = DHCP_DNSSERVER;
			*p++ = DHCP_NISDOMAIN;
			*p++ = DHCP_NISSERVER;
			*p++ = DHCP_NTPSERVER;
			*p++ = DHCP_MTU;
			*p++ = DHCP_ROOTPATH;
			*p++ = DHCP_SIPSERVER;
		}

		*n_params = p - n_params - 1;

		if (options->hostname[0]) {
			if (options->fqdn == FQDN_DISABLE) {
				*p++ = DHCP_HOSTNAME;
				*p++ = l = strlen (options->hostname);
				memcpy (p, options->hostname, l);
				p += l;
			} else {
				/* Draft IETF DHC-FQDN option (81) */
				*p++ = DHCP_FQDN;
				*p++ = (l = strlen (options->hostname)) + 3;
				/* Flags: 0000NEOS
				 * S: 1 => Client requests Server to update A RR in DNS as well as PTR
				 * O: 1 => Server indicates to client that DNS has been updated
				 * E: 1 => Name data is DNS format
				 * N: 1 => Client requests Server to not update DNS
				 */
				*p++ = options->fqdn & 0x9;
				*p++ = 0; /* rcode1, response from DNS server for PTR RR */
				*p++ = 0; /* rcode2, response from DNS server for A RR if S=1 */
				memcpy (p, options->hostname, l);
				p += l;
			}
		}
	}

	if (type != DHCP_DECLINE && type != DHCP_RELEASE) {
		if (options->userclass_len > 0) {
			*p++ = DHCP_USERCLASS;
			*p++ = options->userclass_len;
			memcpy (p, &options->userclass, options->userclass_len);
			p += options->userclass_len;
		}

		if (options->classid_len > 0) {
			*p++ = DHCP_CLASSID;
			*p++ = options->classid_len;
			memcpy (p, options->classid, options->classid_len);
			p += options->classid_len;
		}
	}

	*p++ = DHCP_CLIENTID;
	if (options->clientid_len > 0) {
		*p++ = options->clientid_len + 1;
		*p++ = 0; /* string */
		memcpy (p, options->clientid, options->clientid_len);
		p += options->clientid_len;
#ifdef ENABLE_DUID
	} else if (iface->duid && options->clientid_len != -1) {
		*p++ = iface->duid_length + 5;
		*p++ = 255; /* RFC 4361 */

		/* IAID is 4 bytes, so if the interface name is 4 bytes then use it */
		if (strlen (iface->name) == 4) {
			memcpy (p, iface->name, 4);
		} else {
			/* Name isn't 4 bytes, so use the index */
			ul = htonl (if_nametoindex (iface->name));
			memcpy (p, &ul, 4);
		}
		p += 4;

		memcpy (p, iface->duid, iface->duid_length);
		p += iface->duid_length;
#endif
	} else {
		*p++ = iface->hwlen + 1;
		*p++ = iface->family;
		memcpy (p, iface->hwaddr, iface->hwlen);
		p += iface->hwlen;
	}

	*p++ = DHCP_END;

#ifdef BOOTP_MESSAGE_LENTH_MIN
	/* Some crappy DHCP servers think they have to obey the BOOTP minimum
	 * messag length. They are wrong, but we should still cater for them */
	while (p - m < BOOTP_MESSAGE_LENTH_MIN)
		*p++ = DHCP_PAD;
#endif

	message_length = p - m;

	packet = xmalloc (sizeof (struct udp_dhcp_packet));
	memset (packet, 0, sizeof (struct udp_dhcp_packet));
	make_dhcp_packet (packet, (unsigned char *) message, message_length,
					  from, to);
	free (message);

	logger (LOG_DEBUG, "sending %s with xid 0x%lx",
			dhcp_message[(int) type], xid);
	retval = send_packet (iface, ETHERTYPE_IP, (unsigned char *) packet,
						  message_length + sizeof (struct ip) +
						  sizeof (struct udphdr));
	free (packet);
	return (retval);
}
Exemplo n.º 10
0
static int
parse_kernel_route(const struct rt_msghdr *rtm, struct kernel_route *route)
{
    struct sockaddr *sa;
    char *rta = (char*)rtm + sizeof(struct rt_msghdr);
    uint32_t excluded_flags = 0;

    if(ifindex_lo < 0) {
        ifindex_lo = if_nametoindex("lo0");
        if(ifindex_lo <= 0)
            return -1;
    }

    memset(route, 0, sizeof(*route));
    route->metric = 0;
    route->ifindex = rtm->rtm_index;

#if defined(RTF_IFSCOPE)
    /* Filter out kernel route on OS X */
    excluded_flags |= RTF_IFSCOPE;
#endif
#if defined(RTF_MULTICAST)
    /* Filter out multicast route on others BSD */
    excluded_flags |= RTF_MULTICAST;
#endif
    /* Filter out our own route */
    excluded_flags |= RTF_PROTO2;
    if((rtm->rtm_flags & excluded_flags) != 0)
        return -1;

    /* Prefix */
    if(!(rtm->rtm_addrs & RTA_DST))
        return -1;
    sa = (struct sockaddr *)rta;
    rta += ROUNDUP(sa->sa_len);
    if(sa->sa_family == AF_INET6) {
          struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
        memcpy(route->prefix, &sin6->sin6_addr, 16);
        if(IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)
             || IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr))
           return -1;
    } else if(sa->sa_family == AF_INET) {
        struct sockaddr_in *sin = (struct sockaddr_in *)sa;
#if defined(IN_LINKLOCAL)
        if(IN_LINKLOCAL(ntohl(sin->sin_addr.s_addr)))
            return -1;
#endif
        if(IN_MULTICAST(ntohl(sin->sin_addr.s_addr)))
            return -1;
        v4tov6(route->prefix, (unsigned char *)&sin->sin_addr);
    } else {
        return -1;
    }

    /* Gateway */
    if(!(rtm->rtm_addrs & RTA_GATEWAY))
        return -1;
    sa = (struct sockaddr *)rta;
    rta += ROUNDUP(sa->sa_len);
    if(sa->sa_family == AF_INET6) {
          struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
        memcpy(route->gw, &sin6->sin6_addr, 16);
        if(IN6_IS_ADDR_LINKLOCAL (&sin6->sin6_addr)) {
            route->ifindex = IN6_LINKLOCAL_IFINDEX(sin6->sin6_addr);
            SET_IN6_LINKLOCAL_IFINDEX(sin6->sin6_addr, 0);
        }
    } else if(sa->sa_family == AF_INET) {
        struct sockaddr_in *sin = (struct sockaddr_in *)sa;
        v4tov6(route->gw, (unsigned char *)&sin->sin_addr);
    }
    if((int)route->ifindex == ifindex_lo)
        return -1;

    /* Netmask */
    if((rtm->rtm_addrs & RTA_NETMASK) != 0) {
        sa = (struct sockaddr *)rta;
        rta += ROUNDUP(sa->sa_len);
        if(!v4mapped(route->prefix)) {
            struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
            route->plen = mask2len((unsigned char*)&sin6->sin6_addr, 16);
        } else {
            struct sockaddr_in *sin = (struct sockaddr_in *)sa;
            route->plen = mask2len((unsigned char*)&sin->sin_addr, 4);
        }
    }
    if(v4mapped(route->prefix)) route->plen += 96;
    if(rtm->rtm_flags & RTF_HOST) route->plen = 128;

    return 0;
}
Exemplo n.º 11
0
/* handle address coming or going away */
static int
rtm_dispatch_newdeladdr(struct rtm_dispinfo *di)
{
        Address                 *ap;
        struct ifa_msghdr       *ifam;
        struct sockaddr         *sa;
        struct sockaddr_in      *sin;
        int                     link_local;

/* macro to skip to next RTA; has side-effects */
#define SKIPRTA(ifamsgp, rta, sa)                                       \
        do {                                                            \
                if ((ifamsgp)->ifam_addrs & (rta))                      \
                        (sa) = next_sa((sa));                           \
        } while (0)

        ifam = &((rtmunion_t *)di->di_buf)->ifam;

        assert(ifam->ifam_type == RTM_NEWADDR ||
               ifam->ifam_type == RTM_DELADDR);

        daemon_log(LOG_DEBUG, "%s: %s for iface %d (%s)", __func__,
            ifam->ifam_type == RTM_NEWADDR ? "NEWADDR" : "DELADDR",
            ifam->ifam_index, (ifam->ifam_index == ifindex) ? "ours" : "not ours");

        if (ifam->ifam_index != ifindex)
                return (0);

        if (!(ifam->ifam_addrs & RTA_IFA)) {
                daemon_log(LOG_ERR, "ifa msg has no RTA_IFA.");
                return (0);
        }

        /* skip over rtmsg padding correctly */
        sa = (struct sockaddr *)(ifam + 1);
        SKIPRTA(ifam, RTA_DST, sa);
        SKIPRTA(ifam, RTA_GATEWAY, sa);
        SKIPRTA(ifam, RTA_NETMASK, sa);
        SKIPRTA(ifam, RTA_GENMASK, sa);
        SKIPRTA(ifam, RTA_IFP, sa);

        /*
         * sa now points to RTA_IFA sockaddr; we are only interested
         * in updates for routable addresses.
         */
        if (sa->sa_family != AF_INET) {
                daemon_log(LOG_DEBUG, "%s: RTA_IFA family not AF_INET (=%d)", __func__, sa->sa_family);
                return (0);
        }

        sin = (struct sockaddr_in *)sa;
        link_local = IN_LINKLOCAL(ntohl(sin->sin_addr.s_addr));

        daemon_log(LOG_DEBUG, "%s: %s for %s (%s)", __func__,
            ifam->ifam_type == RTM_NEWADDR ? "NEWADDR" : "DELADDR",
            inet_ntoa(sin->sin_addr), link_local ? "link local" : "routable");

        if (link_local)
                return (0);

        for (ap = addresses; ap; ap = ap->addresses_next) {
                if (ap->address == sin->sin_addr.s_addr)
                        break;
        }
        if (ifam->ifam_type == RTM_DELADDR && ap != NULL) {
                AVAHI_LLIST_REMOVE(Address, addresses, addresses, ap);
                avahi_free(ap);
        }
        if (ifam->ifam_type == RTM_NEWADDR && ap == NULL) {
                ap = avahi_new(Address, 1);
                ap->address = sin->sin_addr.s_addr;
                AVAHI_LLIST_PREPEND(Address, addresses, addresses, ap);
        }

        return (0);
#undef SKIPRTA
}
Exemplo n.º 12
0
int
ni_dhcp_build_message(const ni_dhcp_device_t *dev,
			unsigned int msg_code,
			const ni_addrconf_lease_t *lease,
			ni_buffer_t *msgbuf)
{
	const ni_dhcp_config_t *options = dev->config;
	struct in_addr src_addr, dst_addr;
	ni_dhcp_message_t *message = NULL;

	if (!options || !lease)
		return -1;

	if (IN_LINKLOCAL(ntohl(lease->dhcp.address.s_addr))) {
		ni_error("cannot request a link local address");
		goto failed;
	}

	src_addr.s_addr = dst_addr.s_addr = 0;
	switch (msg_code) {
	case DHCP_DISCOVER:
		if (lease->dhcp.serveraddress.s_addr != 0)
			return -1;
		break;

	case DHCP_REQUEST:
	case DHCP_RELEASE:
	case DHCP_INFORM:
		if (lease->dhcp.address.s_addr == 0 || lease->dhcp.serveraddress.s_addr == 0)
			return -1;
		src_addr = lease->dhcp.address;
		dst_addr = lease->dhcp.serveraddress;
		break;
	}

	/* Reserve some room for the IP and UDP header */
	ni_buffer_reserve_head(msgbuf, sizeof(struct ip) + sizeof(struct udphdr));

	/* Build the message */
	message = ni_buffer_push_tail(msgbuf, sizeof(*message));

	message->op = DHCP_BOOTREQUEST;
	message->hwtype = dev->system.arp_type;
	message->xid = dev->dhcp.xid;
	message->cookie = htonl(MAGIC_COOKIE);
	message->secs = htons(ni_dhcp_device_uptime(dev, 0xFFFF));

	if (dev->fsm.state == NI_DHCP_STATE_BOUND
	 || dev->fsm.state == NI_DHCP_STATE_RENEWING
	 || dev->fsm.state == NI_DHCP_STATE_REBINDING)
		message->ciaddr = lease->dhcp.address.s_addr;

	switch (dev->system.arp_type) {
	case ARPHRD_ETHER:
	case ARPHRD_IEEE802:
		if (dev->system.hwaddr.len > sizeof(message->chaddr)) {
			ni_error("dhcp cannot handle hwaddress length %u",
					dev->system.hwaddr.len);
			goto failed;
		}
		message->hwlen = dev->system.hwaddr.len;
		memcpy(&message->chaddr, dev->system.hwaddr.data, dev->system.hwaddr.len);
		break;

	case ARPHRD_IEEE1394:
	case ARPHRD_INFINIBAND:
		message->hwlen = 0;
		if (message->ciaddr == 0)
			message->flags = htons(BROADCAST_FLAG);
		break;

	default:
		ni_error("dhcp: unknown hardware type %d", dev->system.arp_type);
	}

	ni_dhcp_option_put8(msgbuf, DHCP_MESSAGETYPE, msg_code);

	if (msg_code == DHCP_REQUEST)
		ni_dhcp_option_put16(msgbuf, DHCP_MAXMESSAGESIZE, dev->system.mtu);

	ni_dhcp_option_put(msgbuf, DHCP_CLIENTID,
			options->raw_client_id.data,
			options->raw_client_id.len);

	if (msg_code != DHCP_DECLINE && msg_code != DHCP_RELEASE) {
		if (options->userclass.len > 0)
			ni_dhcp_option_put(msgbuf, DHCP_USERCLASS,
					options->userclass.data,
					options->userclass.len);

		if (options->classid && options->classid[0])
			ni_dhcp_option_puts(msgbuf, DHCP_CLASSID, options->classid);
	}

	if (msg_code == DHCP_DISCOVER || msg_code == DHCP_REQUEST) {
		if (lease->dhcp.address.s_addr)
			ni_dhcp_option_put_ipv4(msgbuf, DHCP_ADDRESS, lease->dhcp.address);
		if (lease->dhcp.lease_time != 0)
			ni_dhcp_option_put32(msgbuf, DHCP_LEASETIME, lease->dhcp.lease_time);
	}

	if (msg_code == DHCP_REQUEST) {
		if (lease->dhcp.serveraddress.s_addr)
			ni_dhcp_option_put_ipv4(msgbuf, DHCP_SERVERIDENTIFIER, lease->dhcp.serveraddress);
	}

	if (msg_code == DHCP_DISCOVER || msg_code == DHCP_INFORM || msg_code == DHCP_REQUEST) {
		unsigned int params_begin;

		if (options->hostname && options->hostname[0]) {
			if (options->fqdn == FQDN_DISABLE) {
				ni_dhcp_option_puts(msgbuf, DHCP_HOSTNAME, options->hostname);
			} else {
				/* IETF DHC-FQDN option(81)
				 * http://tools.ietf.org/html/rfc4702#section-2.1
				 *
				 * Flags: 0000NEOS
				 * S: 1 => Client requests Server to update
				 *         a RR in DNS as well as PTR
				 * O: 1 => Server indicates to client that
				 *         DNS has been updated
				 * E: 1 => Name data is DNS format
				 * N: 1 => Client requests Server to not
				 *         update DNS
				 */
				ni_buffer_putc(msgbuf, DHCP_FQDN);
				ni_buffer_putc(msgbuf, strlen(options->hostname) + 3);
				ni_buffer_putc(msgbuf, options->fqdn & 0x9);
				ni_buffer_putc(msgbuf, 0);	/* from server for PTR RR */
				ni_buffer_putc(msgbuf, 0);	/* from server for A RR if S=1 */
				ni_buffer_put(msgbuf, options->hostname, strlen(options->hostname));
			}
		}

		params_begin = ni_dhcp_option_begin(msgbuf, DHCP_PARAMETERREQUESTLIST);

		if (msg_code == DHCP_DISCOVER) {
			/* dhcpcd says we should include just a single option
			 * in discovery packets.
			 * I'm not convinced this is right, but let's do it
			 * this way.
			 */
			ni_buffer_putc(msgbuf, DHCP_DNSSERVER);
		} else {
			if (msg_code != DHCP_INFORM) {
				ni_buffer_putc(msgbuf, DHCP_RENEWALTIME);
				ni_buffer_putc(msgbuf, DHCP_REBINDTIME);
			}
			ni_buffer_putc(msgbuf, DHCP_NETMASK);
			ni_buffer_putc(msgbuf, DHCP_BROADCAST);

			if (options->flags & DHCP_DO_CSR)
				ni_buffer_putc(msgbuf, DHCP_CSR);
			if (options->flags & DHCP_DO_MSCSR)
				ni_buffer_putc(msgbuf, DHCP_MSCSR);

			/* RFC 3442 states classless static routes should be
			 * before routers and static routes as classless static
			 * routes override them both */
			ni_buffer_putc(msgbuf, DHCP_STATICROUTE);
			ni_buffer_putc(msgbuf, DHCP_ROUTERS);
			ni_buffer_putc(msgbuf, DHCP_HOSTNAME);
			ni_buffer_putc(msgbuf, DHCP_DNSSEARCH);
			ni_buffer_putc(msgbuf, DHCP_DNSDOMAIN);
			ni_buffer_putc(msgbuf, DHCP_DNSSERVER);

			if (options->flags & DHCP_DO_NIS) {
				ni_buffer_putc(msgbuf, DHCP_NISDOMAIN);
				ni_buffer_putc(msgbuf, DHCP_NISSERVER);
			}
			if (options->flags & DHCP_DO_NTP)
				ni_buffer_putc(msgbuf, DHCP_NTPSERVER);
			ni_buffer_putc(msgbuf, DHCP_MTU);
			ni_buffer_putc(msgbuf, DHCP_ROOTPATH);
			ni_buffer_putc(msgbuf, DHCP_SIPSERVER);
			ni_buffer_putc(msgbuf, DHCP_LPRSERVER);
			ni_buffer_putc(msgbuf, DHCP_LOGSERVER);
			ni_buffer_putc(msgbuf, DHCP_NETBIOSNAMESERVER);
			ni_buffer_putc(msgbuf, DHCP_NETBIOSDDSERVER);
			ni_buffer_putc(msgbuf, DHCP_NETBIOSNODETYPE);
			ni_buffer_putc(msgbuf, DHCP_NETBIOSSCOPE);
		}

		ni_dhcp_option_end(msgbuf, params_begin);
	}
	ni_buffer_putc(msgbuf, DHCP_END);

#ifdef BOOTP_MESSAGE_LENGTH_MIN
	ni_buffer_pad(msgbuf, BOOTP_MESSAGE_LENGTH_MIN, DHCP_PAD);
#endif

	if (ni_capture_build_udp_header(msgbuf, src_addr, DHCP_CLIENT_PORT, dst_addr, DHCP_SERVER_PORT) < 0) {
		ni_error("unable to build packet header");
		goto failed;
	}

	return 0;

failed:
	return -1;
}
Exemplo n.º 13
0
int
ni_dhcp4_build_message(const ni_dhcp4_device_t *dev, unsigned int msg_code,
			const ni_addrconf_lease_t *lease, ni_buffer_t *msgbuf)
{
	const ni_dhcp4_config_t *options = dev->config;
	struct in_addr src_addr, dst_addr;
	int renew = dev->fsm.state == NI_DHCP4_STATE_RENEWING && msg_code == DHCP4_REQUEST;

	if (!options || !lease) {
		ni_error("%s: %s: %s: missing %s %s", __func__,
				dev->ifname, ni_dhcp4_message_name(msg_code),
				options? "" : "options", lease ? "" : "lease");
		return -1;
	}

	if (IN_LINKLOCAL(ntohl(lease->dhcp4.address.s_addr))) {
		ni_error("%s: cannot request a link local address", dev->ifname);
		goto failed;
	}

	/* Reserve some room for the IP and UDP header */
	if (!renew)
		ni_buffer_reserve_head(msgbuf, sizeof(struct ip) + sizeof(struct udphdr));

	src_addr.s_addr = dst_addr.s_addr = 0;
	switch (msg_code) {
	case DHCP4_INFORM:
		if (__ni_dhcp4_build_msg_inform(dev, lease, msgbuf) < 0)
			goto failed;
		break;

	case DHCP4_DISCOVER:
		if (__ni_dhcp4_build_msg_discover(dev, lease, msgbuf) < 0)
			goto failed;
		break;

	case DHCP4_DECLINE:
		if (__ni_dhcp4_build_msg_decline(dev, lease, msgbuf) < 0)
			goto failed;
		break;

	case DHCP4_RELEASE:
		if (__ni_dhcp4_build_msg_release(dev, lease, msgbuf) < 0)
			goto failed;
		break;

	case DHCP4_REQUEST:
		switch (dev->fsm.state) {
		case NI_DHCP4_STATE_REQUESTING:
			src_addr.s_addr = 0;
			dst_addr.s_addr = 0;

			if (__ni_dhcp4_build_msg_request_offer(dev, lease, msgbuf) < 0)
				goto failed;
			break;

		case NI_DHCP4_STATE_RENEWING:
			src_addr = lease->dhcp4.address;
			dst_addr = lease->dhcp4.server_id;

			if (__ni_dhcp4_build_msg_request_renew(dev, lease, msgbuf) < 0)
				goto failed;
			break;

		case NI_DHCP4_STATE_REBINDING:
			src_addr = lease->dhcp4.address;
			dst_addr.s_addr = 0;

			if (__ni_dhcp4_build_msg_request_rebind(dev, lease, msgbuf) < 0)
				goto failed;
			break;

		case NI_DHCP4_STATE_REBOOT:
			src_addr.s_addr = 0;
			dst_addr.s_addr = 0;

			if (__ni_dhcp4_build_msg_request_reboot(dev, lease, msgbuf) < 0)
				goto failed;
			break;

		default:
			goto failed;
		}
		break;

	default:
		goto failed;
	}

	ni_buffer_putc(msgbuf, DHCP4_END);

#ifdef BOOTP_MESSAGE_LENGTH_MIN
	ni_buffer_pad(msgbuf, BOOTP_MESSAGE_LENGTH_MIN, DHCP4_PAD);
#endif

	if (!renew && ni_capture_build_udp_header(msgbuf, src_addr,
			DHCP4_CLIENT_PORT, dst_addr, DHCP4_SERVER_PORT) < 0) {
		ni_error("%s: unable to build packet header", dev->ifname);
		goto failed;
	}

	return 0;
failed:
	return -1;
}