#include #include #include #include #include #include #include #include #include #include #include #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; }