File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / freevrrpd / vrrp_netgraph.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 (7 years, 4 months ago) by misho
Branches: freevrrpd, MAIN
CVS tags: v1_1, HEAD
freevrrpd 1.1

#include <netgraph.h>
#include <netgraph/ng_message.h>
#include <netgraph/ng_ether.h>
#include <netgraph/ng_eiface.h>
#include <syslog.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "vrrp_netgraph.h"
#include "vrrp_proto.h"
#include "vrrp_functions.h"

struct ng_mesg *vrrp_netgraph_get_node_list(int);

int vrrp_netgraph_open(int *ng_control_socket, int *ng_data_socket) {
	if (NgMkSockNode(NULL, ng_control_socket, ng_data_socket) < 0) {
		syslog(LOG_ERR, "cannot create a netgraph socket: %s", strerror(errno));
		return -1;
	}

	return 0;
}

void vrrp_netgraph_close(int ng_control_socket, int ng_data_socket) {
	close(ng_control_socket);
	close(ng_data_socket);

	return;
}

int vrrp_netgraph_bridge_create(char *ifname) {
	struct ngm_mkpeer mkp;
	struct ngm_name name;
	struct ngm_connect connect;
	char path[256];
	int ng_control_socket, ng_data_socket;

	if (vrrp_netgraph_open(&ng_control_socket, &ng_data_socket) < 0)
		return -1;
	
	snprintf(mkp.type, sizeof(mkp.type), "bridge");
	snprintf(mkp.ourhook, sizeof(mkp.ourhook), "lower");
	snprintf(mkp.peerhook, sizeof(mkp.peerhook), "link0");
	snprintf(path, sizeof(path), "%s:", ifname);

	if (NgSendMsg(ng_control_socket, path, NGM_GENERIC_COOKIE, NGM_MKPEER, &mkp, sizeof(mkp)) < 0) {
		vrrp_netgraph_close(ng_control_socket, ng_data_socket);
		return -1;
	}

	snprintf(name.name, sizeof(name.name), "%s_%sbridge", VRRP_NETGRAPH_BASENAME, ifname);
	snprintf(path, sizeof(path), "%s:lower", ifname);
	if (NgSendMsg(ng_control_socket, path, NGM_GENERIC_COOKIE, NGM_NAME, &name, sizeof(name)) < 0) {
		vrrp_netgraph_close(ng_control_socket, ng_data_socket);
		return -1;
	}

	snprintf(connect.path, sizeof(connect.path), "%s_%sbridge:", VRRP_NETGRAPH_BASENAME, ifname);
	snprintf(connect.ourhook, sizeof(connect.ourhook), "upper");
	snprintf(connect.peerhook, sizeof(connect.peerhook), "link1");
	snprintf(path, sizeof(path), "%s:", ifname);
	if (NgSendMsg(ng_control_socket, path, NGM_GENERIC_COOKIE, NGM_CONNECT, &connect, sizeof(connect)) < 0) {
		vrrp_netgraph_close(ng_control_socket, ng_data_socket);
		return -1;
	}

	vrrp_netgraph_close(ng_control_socket, ng_data_socket);

	return 0;
}

int vrrp_netgraph_get_ethernet_address(int ng_control_socket, char *path, struct ether_addr *eaddr) {
	struct ng_mesg *ngmsg;

	if (NgSendMsg(ng_control_socket, path, NGM_EIFACE_COOKIE, NGM_EIFACE_GET_IFADDRS, NULL, 0) < 0)
		return -1;

	if (NgAllocRecvMsg(ng_control_socket, &ngmsg, NULL) < 0) {
		syslog(LOG_ERR, "cannot get netgraph answer: %s\n", strerror(errno));
		return -1;
	}

	/* XXX SANITY CHECK HERE */
	bcopy(ngmsg->data, eaddr, sizeof(*eaddr));

	free(ngmsg);
	return 0;
}

int vrrp_netgraph_set_ethernet_address(int ng_control_socket, char *path, struct ether_addr *eaddr) {
	if (NgSendMsg(ng_control_socket, path, NGM_EIFACE_COOKIE, NGM_EIFACE_SET, eaddr, sizeof(*eaddr)) < 0)
		return -1;

	return 0;
}

int vrrp_netgraph_create_eiface(char *ng_name, char *ether_name, struct ether_addr *ng_eaddr) {
	struct ngm_mkpeer mkp;
	struct ng_mesg *ngmsg;
	char path[256];
	char name[64];
	struct nodeinfo *ninfo;
	struct namelist *nlist;
	char found = 0;
	int ng_control_socket, ng_data_socket;

	if (vrrp_netgraph_open(&ng_control_socket, &ng_data_socket))
		return -1;

	snprintf(path, sizeof(path), ".");
	snprintf(mkp.type, sizeof(mkp.type), "eiface");
	snprintf(mkp.ourhook, sizeof(mkp.ourhook), "ether");
	snprintf(mkp.peerhook, sizeof(mkp.peerhook), "ether");
	if (NgSendMsg(ng_control_socket, path, NGM_GENERIC_COOKIE, NGM_MKPEER, &mkp, sizeof(mkp)) < 0)
		return -1;

	vrrp_netgraph_close(ng_control_socket, ng_data_socket);

	if (vrrp_netgraph_open(&ng_control_socket, &ng_data_socket))
		return -1;

	/* Get node list for assigning a name to the newly created eiface */
	/* libnetgraph lacks of returning ID/name when creating nodes */
	/* it's a problem... */
	ngmsg = vrrp_netgraph_get_node_list(ng_control_socket);
	if (! ngmsg)
		return -1;

	nlist = (struct namelist *)ngmsg->data;
	ninfo = nlist->nodeinfo;
	while ((nlist->numnames > 0) && (! found)) {
		if (! strcmp(ninfo->type, "eiface")) {
			if (! strcmp(ninfo->name, ""))
				snprintf(ninfo->name, sizeof(ninfo->name), "ngeth0");
			if (! ninfo->hooks) {
				snprintf(path, sizeof(path), "[%X]:", ninfo->id);
				snprintf(ether_name, IFNAMSIZ, "%s", ninfo->name);
				if (vrrp_netgraph_set_ethernet_address(ng_control_socket, path, ng_eaddr) < 0) {
					syslog(LOG_ERR, "cannot set ethernet address to %s: %s\n", ninfo->name, strerror(errno));
					free(ngmsg);
					return -1;
				}
				snprintf(name, sizeof(name), "%s", ng_name);
				if (NgNameNode(ng_control_socket, path, name) < 0) {
					free(ngmsg);
					return -1;
				}
				found = 1;
			}
		}
		ninfo++;
		nlist->numnames--;
	}

	free(ngmsg);

	vrrp_netgraph_close(ng_control_socket, ng_data_socket);

	return 0;
}

int vrrp_netgraph_connect_eiface_to_bridge(int ng_control_socket, char *eiface_name, char *ifname, int *link_number) {
	struct ngm_connect connect;
	char path[256];

	NgSetDebug(10);

	snprintf(path, sizeof(path), "%s:", eiface_name);
	snprintf(connect.path, sizeof(connect.path), "%s_%sbridge:", VRRP_NETGRAPH_BASENAME, ifname);
	snprintf(connect.ourhook, sizeof(connect.ourhook), "ether");
	snprintf(connect.peerhook, sizeof(connect.peerhook), "link%d", *link_number);

	if (NgSendMsg(ng_control_socket, path, NGM_GENERIC_COOKIE, NGM_CONNECT, &connect, sizeof(connect)) < 0) {
		syslog(LOG_ERR, "cannot connect path %s to bridge %s with link%d: %s\n", path, connect.path, *link_number, strerror(errno));
		return -1;
	}

	(*link_number)++;

	return 0;
}

struct ng_mesg *vrrp_netgraph_get_node_list(int ng_control_socket) {
	struct ng_mesg *ngmsg;

	if (NgSendMsg(ng_control_socket, ".", NGM_GENERIC_COOKIE, NGM_LISTNODES, NULL, 0) < 0) {
		syslog(LOG_ERR, "cannot send netgraph message: %s\n", strerror(errno));
		return NULL;
	}

	if (NgAllocRecvMsg(ng_control_socket, &ngmsg, NULL) < 0) {
		syslog(LOG_ERR, "cannot get netgraph answer: %s\n", strerror(errno));
		return NULL;
	}
	
	return ngmsg;
}

int vrrp_netgraph_create_virtualiface(struct vrrp_vr *vr) {
	char eiface_name[256];
	int ng_control_socket, ng_data_socket;

	snprintf(eiface_name, sizeof(eiface_name), "%s_vrid%d", VRRP_NETGRAPH_BASENAME, vr->vr_id);
	if (vrrp_netgraph_create_eiface(eiface_name, vr->viface_name, &vr->ethaddr) < 0) {
		syslog(LOG_ERR, "cannot create an eiface/ether netgraph interface: %s\n", strerror(errno));
		syslog(LOG_ERR, "ng_ether.ko is probably not loaded, use kldload ng_ether.ko before running freevrrpd\n");
		fprintf(stderr, "Please load ng_ether manually with kldload ng_ether.ko or add ng_ether_load=\"YES\" into /boot/loader.conf\n");
		return -1;
	}
	if (vrrp_netgraph_open(&ng_control_socket, &ng_data_socket) < 0)
		return -1;
	if (vrrp_netgraph_connect_eiface_to_bridge(ng_control_socket, eiface_name, vr->vr_if->if_name, &vr->bridge_link_number) < 0)
		return -1;

	vrrp_netgraph_close(ng_control_socket, ng_data_socket);

	return 0;
}

int vrrp_netgraph_shutdown_allnodes(void) {
	struct nodeinfo *ninfo;
	struct namelist *nlist;
	struct ng_mesg *ngmsg;
	char path[256];
	int ng_control_socket, ng_data_socket;

	if (vrrp_netgraph_open(&ng_control_socket, &ng_data_socket) < 0)
		return -1;

	ngmsg = vrrp_netgraph_get_node_list(ng_control_socket);
	if (! ngmsg)
		return -1;

	nlist = (struct namelist *)ngmsg->data;
	ninfo = nlist->nodeinfo;
	while (nlist->numnames > 0) {
		if (! strncmp(ninfo->name, VRRP_NETGRAPH_BASENAME, strlen(VRRP_NETGRAPH_BASENAME))) {
			snprintf(path, sizeof(path), "%s:", ninfo->name);
			vrrp_netgraph_shutdown(ng_control_socket, path);
		}
		ninfo++;	
		nlist->numnames--;
	}

	free(ngmsg);
	vrrp_netgraph_close(ng_control_socket, ng_data_socket);

	return 0;
}

int vrrp_netgraph_shutdown(int ng_control_socket, char *path) {
	if (NgSendMsg(ng_control_socket, path, NGM_GENERIC_COOKIE, NGM_SHUTDOWN, NULL, 0) < 0) {
		syslog(LOG_ERR, "cannot shutdown netgraph path %s: %s\n", path, strerror(errno));
		return -1;
	}

	return 0;
}

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