File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / freevrrpd / vrrp_misc.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 14 12:01:54 2017 UTC (6 years, 11 months ago) by misho
Branches: freevrrpd, MAIN
CVS tags: v1_1, HEAD
freevrrpd 1.1

/*
 * Copyright (c) 2001,2002 Sebastien Petit <spe@bsdfr.org>
 *
 * Redistribution and use in source forms, with and without modification,
 * are permitted provided that the following conditions are met:
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution. Obviously, it
 *    would be nice if you gave credit where credit is due but requiring it
 *    would be too onerous.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by Sebastien Petit.
 * 4. Neither the name of its contributors may be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $Id: vrrp_misc.c,v 1.1.1.1 2017/06/14 12:01:54 misho Exp $
 */

#include <errno.h>
#include "vrrp_misc.h"
#ifdef ENABLE_VRRP_AH
#include "vrrp_ah.h"
#endif

/* this code is based on ifconfig.c */
#define ROUNDUP(a) \
((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))

void 
rt_xaddrs(caddr_t cp, caddr_t cplim, struct rt_addrinfo * rtinfo)
{
	struct sockaddr *sa;
	int             i;

	memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info));
	for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
		if ((rtinfo->rti_addrs & (1 << i)) == 0)
			continue;
		rtinfo->rti_info[i] = sa = (struct sockaddr *) cp;
		ADVANCE(cp, sa);
	}
}

char 
vrrp_misc_get_if_infos(char *if_name, struct ether_addr * ethaddr, struct in_addr * ip_addrs, int *size)
{
	int             addrcount;
	struct if_msghdr *ifm, *nextifm;
	struct ifa_msghdr *ifam;
	struct sockaddr_dl *sdl;
	struct sockaddr_in *sin;
	char           *buf, *lim, *next;
	char            name[64];
	struct rt_addrinfo info;
	size_t          needed;
	int             mib[6];
	int             sizes = *size;
	char            myinterface = 0;

	mib[0] = CTL_NET;
	mib[1] = PF_ROUTE;
	mib[2] = 0;
	mib[3] = AF_INET;
	mib[4] = NET_RT_IFLIST;
	mib[5] = 0;

	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
		syslog(LOG_ERR, "iflist-sysctl-estimate: %s", strerror(errno));
		return -1;
	}
	if ((buf = malloc(needed)) == NULL) {
		syslog(LOG_ERR, "malloc: %s", strerror(errno));
		return -1;
	}
	if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
		syslog(LOG_ERR, "actual retrieval of interface table: %s", strerror(errno));
		free(buf);
		return -1;
	}
	lim = buf + needed;

	next = buf;
	while (next < lim) {
		*size = sizes;
		ifm = (struct if_msghdr *) next;
		if (ifm->ifm_type == RTM_IFINFO) {
			sdl = (struct sockaddr_dl *) (ifm + 1);
			strncpy(name, sdl->sdl_data, sdl->sdl_nlen);
			name[sdl->sdl_nlen] = '\0';
		} else {
			syslog(LOG_ERR, "there is an error: ifm->ifm_type != RTM_INFO");
			free(buf);
			return -1;
		}
		if (strlen(name) != sdl->sdl_nlen)
			continue;
		if (strncmp(name, sdl->sdl_data, sdl->sdl_nlen) != 0)
			continue;
		myinterface = (sdl->sdl_nlen == strlen(if_name)) && (!strncmp(if_name, sdl->sdl_data, sdl->sdl_nlen));
		if (ip_addrs != NULL && size != NULL && *size > 0) {
			next += ifm->ifm_msglen;
			ifam = NULL;
			addrcount = 0;
			while (next < lim) {
				nextifm = (struct if_msghdr *) next;
				if (nextifm->ifm_type != RTM_NEWADDR)
					break;
				ifam = (struct ifa_msghdr *) nextifm;
				info.rti_addrs = ifam->ifam_addrs;
				rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam, &info);
				sin = (struct sockaddr_in *) info.rti_info[RTAX_IFA];
				if (myinterface)
					ip_addrs[addrcount] = sin->sin_addr;
				addrcount++;
				if (*size <= addrcount)
					break;
				next += nextifm->ifm_msglen;
			}
			*size = addrcount;
		}
		if (ethaddr != NULL)
			if (myinterface)
				bcopy(LLADDR(sdl), ethaddr, sizeof(struct ether_addr));
		if (myinterface)
			break;
	}
	free(buf);
	return 0;
}

char 
vrrp_misc_get_vlan_infos(struct vrrp_vr *vr)
{
	struct sockaddr_dl *sdl;
	struct if_msghdr *ifm;
	char           *buf, *lim, *next;
	char            name[64];
	size_t          needed;
	int             mib[6];

	if (! vr) {
		syslog(LOG_ERR, "vr is NULL. cannot vrrp_misc_get_vlan_infos");
		return -1;
	}
	mib[0] = CTL_NET;
	mib[1] = PF_ROUTE;
	mib[2] = 0;
	mib[3] = AF_INET;
	mib[4] = NET_RT_IFLIST;
	mib[5] = 0;

	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
		syslog(LOG_ERR, "iflist-sysctl-estimate: %s", strerror(errno));
		return -1;
	}
	if ((buf = malloc(needed)) == NULL) {
		syslog(LOG_ERR, "malloc: %s", strerror(errno));
		return -1;
	}
	if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
		syslog(LOG_ERR, "actual retrieval of interface table: %s", strerror(errno));
		free(buf);
		return -1;
	}
	lim = buf + needed;

	next = buf;
	while (next < lim) {
		ifm = (struct if_msghdr *) next;
		if (ifm->ifm_type == RTM_IFINFO) {
			sdl = (struct sockaddr_dl *) (ifm + 1);
			strncpy(name, sdl->sdl_data, sdl->sdl_nlen);
			name[sdl->sdl_nlen] = '\0';
			if (! bcmp(LLADDR(sdl), &vr->vr_if->ethaddr, sizeof(struct ether_addr))) {
				vrrp_vlanlist_add(vr, name);
			}
		}
		else
			if (ifm->ifm_type != RTM_NEWADDR) {
				syslog(LOG_ERR, "there is an error: ifm->ifm_type != RTM_INFO");
				free(buf);
				return -1;
			}
		next += ifm->ifm_msglen;
	}
	free(buf);
	return 0;
}
int 
vrrp_misc_get_priority(struct vrrp_vr * vr)
{
	u_char          i = 0, j = 0;

	if (vr->cnt_ip == vr->vr_if->nb_ip) {
		while (j < vr->cnt_ip) {
			while (i < vr->vr_if->nb_ip) {
				if (vr->vr_if->ip_addrs[j].s_addr == vr->vr_ip[i].addr.s_addr)
					break;
				i++;
			}
			if (i >= vr->vr_if->nb_ip)
				return VRRP_PRIORITY_DEFAULT;
			j++;
		}
		return VRRP_PRIORITY_MASTER;
	} else
		return VRRP_PRIORITY_DEFAULT;
}

u_short 
vrrp_misc_compute_checksum(u_short * addr, register int len)
{
	int             nleft = len;
	const u_short * w = addr;
	u_short         answer;
	int             sum = 0;

	while (nleft > 1) {
		sum += *w++;
		nleft -= 2;
	}
	if (nleft == 1)
		sum += htons(*(u_char *) w << 8);
	sum = (sum >> 16) + (sum & 0xFFFF);
	sum += (sum >> 16);
	answer = ~sum;

	return answer;
}

char 
vrrp_misc_calcul_tminterval(struct timeval * timer, u_int interval)
{
	struct timeval  tm;

	if (gettimeofday(&tm, NULL) == -1) {
		syslog(LOG_ERR, "cannot get time with gettimeofday: %s", strerror(errno));
		return -1;
	}
	timer->tv_sec = tm.tv_sec + interval;
	timer->tv_usec = tm.tv_usec;

	return 0;
}

char 
vrrp_misc_calcul_tmrelease(struct timeval * timer, struct timeval * interval)
{
	struct timeval  tm;

	interval->tv_sec = 0;
	interval->tv_usec = 0;
	if (gettimeofday(&tm, NULL) == -1) {
		syslog(LOG_ERR, "cannot get time with gettimeofday: %s", strerror(errno));
		return -1;
	}
	if (tm.tv_sec < timer->tv_sec || (tm.tv_sec == timer->tv_sec && tm.tv_usec < timer->tv_usec)) {
		interval->tv_sec = timer->tv_sec - tm.tv_sec;
		if (timer->tv_usec < tm.tv_usec) {
			interval->tv_sec--;
			interval->tv_usec = (timer->tv_usec + 1000000) - tm.tv_usec;
		} else
			interval->tv_usec = timer->tv_usec - tm.tv_usec;
	}
	return 0;
}

u_short 
vrrp_misc_vphdr_len(struct vrrp_hdr * vph)
{
	return (sizeof(struct vrrp_hdr) + (vph->cnt_ip << 2) + VRRP_AUTH_DATA_LEN);
}

char 
vrrp_misc_check_vrrp_packet(struct vrrp_vr * vr, char *packet, ssize_t packetSize)
{
	struct ip      *iph = (struct ip *) packet;
#ifdef ENABLE_VRRP_AH
	struct vrrp_hdr *vph = (struct vrrp_hdr *) & packet[sizeof(struct ip)+vrrp_ah_ahhdr_len(vr)];
#else
	struct vrrp_hdr *vph = (struct vrrp_hdr *) & packet[sizeof(struct ip)];
#endif
	struct in_addr *ip_addrs = (struct in_addr *) & packet[sizeof(struct ip) + sizeof(struct vrrp_hdr)];
	char           *password = NULL;
	int             error = 0;
	int             cpt, cpt2;
	char            detected;
	u_short         checksum_orig = vph->csum;

	if (iph->ip_ttl != VRRP_MULTICAST_TTL) {
		syslog(LOG_ERR, "ip ttl of vrrp packet isn't set to 255. Packet is discarded !");
		return -1;
	}
	if (vph->vrrp_v != VRRP_PROTOCOL_VERSION) {
		syslog(LOG_ERR, "vrrp version of vrrp packet is not valid or compatible with this daemon. Packet is discarded !");
		return -1;
	}
	if (packetSize < sizeof(struct ip) + vrrp_misc_vphdr_len(vph)) {
		syslog(LOG_ERR, "invalid vrrp packet received (invalid size). Packet is discarded !");
		return -1;
	}
	vph->csum = 0;
	if (vrrp_misc_compute_checksum((u_short *) vph, vrrp_misc_vphdr_len(vph)) != checksum_orig) {
		syslog(LOG_ERR, "checksum of vrrp packet is invalid. Packet is discarded !");
		return -1;
	}
	vph->csum = checksum_orig;
	if (vph->vr_id != vr->vr_id)
		return -1;
	if (vph->cnt_ip != vr->cnt_ip)
		error = 1;
	else {
		for (cpt = 0; cpt < vph->cnt_ip; cpt++) {
			detected = 0;
			for (cpt2 = 0; cpt2 < vr->cnt_ip; cpt2++) {
				if (ip_addrs[cpt].s_addr == vr->vr_ip[cpt2].addr.s_addr)
					detected = 1;
			}
			if (! detected) {
				syslog(LOG_ERR, "error to 1 because not detected");
				error = 1;
				break;
			}
		}
	}
	if (error == 1) {
		syslog(LOG_ERR, "detection of misconfigured server on the network for vrid = %u and priority = %u", vph->vr_id, vph->priority);
		if (vph->priority != VRRP_PRIORITY_MASTER) {
			syslog(LOG_ERR, "this server is not a master virtual router. Packet is discarded !");
			return -1;
		}
	}
	if (vph->adv_int != vr->adv_int) {
		syslog(LOG_ERR, "the advertisement interval set on received vrrp packet isn't same localy. Packet is discarded !");
		return -1;
	}
#ifdef ENABLE_VRRP_AH
	/* checking function */
	vrrp_ah_check_ahhdr(packet,vr);
#endif
	/* Verification of Authentification */
	password = (char *)&ip_addrs[vph->cnt_ip];
	if (vr->auth_type == 1 && strncmp(vr->password, password, 8)) {
		syslog(LOG_ERR, "authentification incorrect in a received vrrp packet. Packet is discarded !");
		return -1;
	}
	return 0;
}

void 
vrrp_misc_quit(int coderet)
{
	exit(coderet);
}

struct vrrp_if *
vrrp_misc_search_if_entry(char *name)
{
	int             cpt = 0;

	while (cpt < VRRP_PROTOCOL_MAX_VRID) {
		if (vr_ptr[cpt] == NULL)
			break;
		if (!strncmp(vr_ptr[cpt]->vr_if->if_name, name, strlen(name)))
			return vr_ptr[cpt]->vr_if;
		cpt++;
	}

	return NULL;
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>