/* $OpenBSD: if.c,v 1.165 2007/07/06 14:00:59 naddy Exp $ */
/* $NetBSD: if_compat.c,v 1.2 2008/06/18 09:06:27 yamt Exp $ */
/*
* Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or 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.
* 3. Neither the name of the project nor the names 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 PROJECT 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 PROJECT 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.
*/
/*
* Copyright (c) 1980, 1986, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or 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.
* 3. Neither the name of the University nor the names 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.
*
* @(#)if.c 8.3 (Berkeley) 1/4/94
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: if_compat.c,v 1.2 2008/06/18 09:06:27 yamt Exp $");
#include "pf.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_var.h>
#include <net/if_compat.h>
#if NPF > 0
#include <net/pfvar.h>
#endif
#if 0 /* XXX unused - remove later */
static int if_getgroup(void *, struct ifnet *);
static int if_getgroupmembers(void *);
static int if_group_egress_build(void);
#endif
TAILQ_HEAD(, ifg_group) ifg_head = TAILQ_HEAD_INITIALIZER(ifg_head);
void
if_init_groups(struct ifnet *ifp)
{
struct ifg_list_head *ifgh;
ifgh = malloc(sizeof(struct ifg_list_head), M_TEMP, M_WAITOK);
TAILQ_INIT(ifgh);
ifp->if_pf_groups = ifgh;
}
void
if_destroy_groups(struct ifnet *ifp)
{
struct ifg_list_head *ifgh = if_get_groups(ifp);
free(ifgh, M_TEMP);
}
struct ifg_list_head *
if_get_groups(struct ifnet *ifp)
{
return (ifp->if_pf_groups);
}
/*
* Create interface group without members.
*/
struct ifg_group *
if_creategroup(const char *groupname)
{
struct ifg_group *ifg = NULL;
if ((ifg = (struct ifg_group *)malloc(sizeof(struct ifg_group),
M_TEMP, M_NOWAIT)) == NULL)
return (NULL);
strlcpy(ifg->ifg_group, groupname, sizeof(ifg->ifg_group));
ifg->ifg_refcnt = 0;
ifg->ifg_carp_demoted = 0;
TAILQ_INIT(&ifg->ifg_members);
#if NPF > 0
pfi_attach_ifgroup(ifg);
#endif
TAILQ_INSERT_TAIL(&ifg_head, ifg, ifg_next);
return (ifg);
}
/*
* Add a group to an interface.
*/
int
if_addgroup(struct ifnet *ifp, const char *groupname)
{
struct ifg_list_head *ifgh = if_get_groups(ifp);
struct ifg_list *ifgl;
struct ifg_group *ifg = NULL;
struct ifg_member *ifgm;
if (groupname[0] && groupname[strlen(groupname) - 1] >= '0' &&
groupname[strlen(groupname) - 1] <= '9')
return (EINVAL);
TAILQ_FOREACH(ifgl, ifgh, ifgl_next)
if (!strcmp(ifgl->ifgl_group->ifg_group, groupname))
return (EEXIST);
if ((ifgl = (struct ifg_list *)malloc(sizeof(struct ifg_list), M_TEMP,
M_NOWAIT)) == NULL)
return (ENOMEM);
if ((ifgm = (struct ifg_member *)malloc(sizeof(struct ifg_member),
M_TEMP, M_NOWAIT)) == NULL) {
free(ifgl, M_TEMP);
return (ENOMEM);
}
TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
if (!strcmp(ifg->ifg_group, groupname))
break;
if (ifg == NULL && (ifg = if_creategroup(groupname)) == NULL) {
free(ifgl, M_TEMP);
free(ifgm, M_TEMP);
return (ENOMEM);
}
ifg->ifg_refcnt++;
ifgl->ifgl_group = ifg;
ifgm->ifgm_ifp = ifp;
TAILQ_INSERT_TAIL(&ifg->ifg_members, ifgm, ifgm_next);
TAILQ_INSERT_TAIL(ifgh, ifgl, ifgl_next);
#if NPF > 0
pfi_group_change(groupname);
#endif
return (0);
}
/*
* Remove a group from an interface.
*/
int
if_delgroup(struct ifnet *ifp, const char *groupname)
{
struct ifg_list_head *ifgh = if_get_groups(ifp);
struct ifg_list *ifgl;
struct ifg_member *ifgm;
TAILQ_FOREACH(ifgl, ifgh, ifgl_next)
if (!strcmp(ifgl->ifgl_group->ifg_group, groupname))
break;
if (ifgl == NULL)
return (ENOENT);
TAILQ_REMOVE(ifgh, ifgl, ifgl_next);
TAILQ_FOREACH(ifgm, &ifgl->ifgl_group->ifg_members, ifgm_next)
if (ifgm->ifgm_ifp == ifp)
break;
if (ifgm != NULL) {
TAILQ_REMOVE(&ifgl->ifgl_group->ifg_members, ifgm, ifgm_next);
free(ifgm, M_TEMP);
}
if (--ifgl->ifgl_group->ifg_refcnt == 0) {
TAILQ_REMOVE(&ifg_head, ifgl->ifgl_group, ifg_next);
#if NPF > 0
pfi_detach_ifgroup(ifgl->ifgl_group);
#endif
free(ifgl->ifgl_group, M_TEMP);
}
free(ifgl, M_TEMP);
#if NPF > 0
pfi_group_change(groupname);
#endif
return (0);
}
#if 0
/*
* Stores all groups from an interface in memory pointed
* to by data.
*/
static int
if_getgroup(void *data, struct ifnet *ifp)
{
int len, error;
struct ifg_list_head *ifgh = if_get_groups(ifp);
struct ifg_list *ifgl;
struct ifg_req ifgrq, *ifgp;
struct ifgroupreq *ifgr = (struct ifgroupreq *)data;
if (ifgr->ifgr_len == 0) {
TAILQ_FOREACH(ifgl, ifgh, ifgl_next)
ifgr->ifgr_len += sizeof(struct ifg_req);
return (0);
}
len = ifgr->ifgr_len;
ifgp = ifgr->ifgr_groups;
TAILQ_FOREACH(ifgl, ifgh, ifgl_next) {
if (len < sizeof(ifgrq))
return (EINVAL);
bzero(&ifgrq, sizeof ifgrq);
strlcpy(ifgrq.ifgrq_group, ifgl->ifgl_group->ifg_group,
sizeof(ifgrq.ifgrq_group));
if ((error = copyout(&ifgrq, ifgp, sizeof(struct ifg_req))))
return (error);
len -= sizeof(ifgrq);
ifgp++;
}
return (0);
}
/*
* Stores all members of a group in memory pointed to by data.
*/
static int
if_getgroupmembers(void *data)
{
struct ifgroupreq *ifgr = (struct ifgroupreq *)data;
struct ifg_group *ifg;
struct ifg_member *ifgm;
struct ifg_req ifgrq, *ifgp;
int len, error;
TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
if (!strcmp(ifg->ifg_group, ifgr->ifgr_name))
break;
if (ifg == NULL)
return (ENOENT);
if (ifgr->ifgr_len == 0) {
TAILQ_FOREACH(ifgm, &ifg->ifg_members, ifgm_next)
ifgr->ifgr_len += sizeof(ifgrq);
return (0);
}
len = ifgr->ifgr_len;
ifgp = ifgr->ifgr_groups;
TAILQ_FOREACH(ifgm, &ifg->ifg_members, ifgm_next) {
if (len < sizeof(ifgrq))
return (EINVAL);
bzero(&ifgrq, sizeof ifgrq);
strlcpy(ifgrq.ifgrq_member, ifgm->ifgm_ifp->if_xname,
sizeof(ifgrq.ifgrq_member));
if ((error = copyout(&ifgrq, ifgp, sizeof(struct ifg_req))))
return (error);
len -= sizeof(ifgrq);
ifgp++;
}
return (0);
}
void
if_group_routechange(struct sockaddr *dst, struct sockaddr *mask)
{
switch (dst->sa_family) {
case AF_INET:
if (satosin(dst)->sin_addr.s_addr == INADDR_ANY)
if_group_egress_build();
break;
#ifdef INET6
case AF_INET6:
if (IN6_ARE_ADDR_EQUAL(&(satosin6(dst))->sin6_addr,
&in6addr_any) &&
mask && IN6_ARE_ADDR_EQUAL(&(satosin6(mask))->sin6_addr,
&in6addr_any))
if_group_egress_build();
break;
#endif
}
}
static int
if_group_egress_build(void)
{
struct ifg_group *ifg;
struct ifg_member *ifgm, *next;
struct sockaddr_in sa_in;
#ifdef INET6
struct sockaddr_in6 sa_in6;
#endif
struct radix_node *rn;
struct rtentry *rt;
TAILQ_FOREACH(ifg, &ifg_head, ifg_next)
if (!strcmp(ifg->ifg_group, IFG_EGRESS))
break;
if (ifg != NULL)
for (ifgm = TAILQ_FIRST(&ifg->ifg_members); ifgm; ifgm = next) {
next = TAILQ_NEXT(ifgm, ifgm_next);
if_delgroup(ifgm->ifgm_ifp, IFG_EGRESS);
}
bzero(&sa_in, sizeof(sa_in));
sa_in.sin_len = sizeof(sa_in);
sa_in.sin_family = AF_INET;
if ((rn = rt_lookup(sintosa(&sa_in), sintosa(&sa_in), 0)) != NULL) {
do {
rt = (struct rtentry *)rn;
if (rt->rt_ifp)
if_addgroup(rt->rt_ifp, IFG_EGRESS);
#ifndef SMALL_KERNEL
rn = rn_mpath_next(rn);
#else
rn = NULL;
#endif
} while (rn != NULL);
}
#ifdef INET6
bcopy(&sa6_any, &sa_in6, sizeof(sa_in6));
if ((rn = rt_lookup(sin6tosa(&sa_in6), sin6tosa(&sa_in6), 0)) != NULL) {
do {
rt = (struct rtentry *)rn;
if (rt->rt_ifp)
if_addgroup(rt->rt_ifp, IFG_EGRESS);
#ifndef SMALL_KERNEL
rn = rn_mpath_next(rn);
#else
rn = NULL;
#endif
} while (rn != NULL);
}
#endif
return (0);
}
#endif