freebsd-dev/contrib/ipfilter/ip_fil.c
Cy Schubert 3820c3aa3f calloc() and realloc() modernization.
This commit replaces calloc calls, which called calloc() as if it were
malloc() by allocating a multiple of objects as a sizeof multiplied by
the number of objects. The patch rectifies this by calling calloc() as
it was meant to be called.

This commit also replaces realloc() with reallocarray() in a similar
fashion as above. Instead of calculating the memory to reallocated
(changed) by multiplying sizeof by the number of objects, the sizeof
and number are passed as separate arguments to reallocarray(), letting
reallocarray() do the multiplication instead. Like the calloc()
adjustment above, this is approach is cleaner and more elegant than
than the previous code.

This has been tested on my production firewall and a laptop (also
running ipfilter).

Submitted by:	pfg
MFC after:	6 weeks
2017-03-16 04:40:07 +00:00

885 lines
16 KiB
C

/* $FreeBSD$ */
/*
* Copyright (C) 2012 by Darren Reed.
*
* See the IPFILTER.LICENCE file for details on licencing.
*
* $Id$
*/
#if !defined(lint)
static const char sccsid[] = "@(#)ip_fil.c 2.41 6/5/96 (C) 1993-2000 Darren Reed";
static const char rcsid[] = "@(#)$Id$";
#endif
#include "ipf.h"
#include "md5.h"
#include "ipt.h"
ipf_main_softc_t ipfmain;
static struct ifnet **ifneta = NULL;
static int nifs = 0;
struct rtentry;
static void ipf_setifpaddr __P((struct ifnet *, char *));
void init_ifp __P((void));
#if defined(__sgi) && (IRIX < 60500)
static int no_output __P((struct ifnet *, struct mbuf *,
struct sockaddr *));
static int write_output __P((struct ifnet *, struct mbuf *,
struct sockaddr *));
#else
# if TRU64 >= 1885
static int no_output __P((struct ifnet *, struct mbuf *,
struct sockaddr *, struct rtentry *, char *));
static int write_output __P((struct ifnet *, struct mbuf *,
struct sockaddr *, struct rtentry *, char *));
# else
static int no_output __P((struct ifnet *, struct mbuf *,
struct sockaddr *, struct rtentry *));
static int write_output __P((struct ifnet *, struct mbuf *,
struct sockaddr *, struct rtentry *));
# endif
#endif
struct ifaddr {
struct sockaddr_storage ifa_addr;
};
int
ipfattach(softc)
ipf_main_softc_t *softc;
{
return 0;
}
int
ipfdetach(softc)
ipf_main_softc_t *softc;
{
return 0;
}
/*
* Filter ioctl interface.
*/
int
ipfioctl(softc, dev, cmd, data, mode)
ipf_main_softc_t *softc;
int dev;
ioctlcmd_t cmd;
caddr_t data;
int mode;
{
int error = 0, unit = 0, uid;
uid = getuid();
unit = dev;
SPL_NET(s);
error = ipf_ioctlswitch(softc, unit, data, cmd, mode, uid, NULL);
if (error != -1) {
SPL_X(s);
return error;
}
SPL_X(s);
return error;
}
void
ipf_forgetifp(softc, ifp)
ipf_main_softc_t *softc;
void *ifp;
{
register frentry_t *f;
WRITE_ENTER(&softc->ipf_mutex);
for (f = softc->ipf_acct[0][softc->ipf_active]; (f != NULL);
f = f->fr_next)
if (f->fr_ifa == ifp)
f->fr_ifa = (void *)-1;
for (f = softc->ipf_acct[1][softc->ipf_active]; (f != NULL);
f = f->fr_next)
if (f->fr_ifa == ifp)
f->fr_ifa = (void *)-1;
for (f = softc->ipf_rules[0][softc->ipf_active]; (f != NULL);
f = f->fr_next)
if (f->fr_ifa == ifp)
f->fr_ifa = (void *)-1;
for (f = softc->ipf_rules[1][softc->ipf_active]; (f != NULL);
f = f->fr_next)
if (f->fr_ifa == ifp)
f->fr_ifa = (void *)-1;
RWLOCK_EXIT(&softc->ipf_mutex);
ipf_nat_sync(softc, ifp);
ipf_lookup_sync(softc, ifp);
}
static int
#if defined(__sgi) && (IRIX < 60500)
no_output(ifp, m, s)
#else
# if TRU64 >= 1885
no_output (ifp, m, s, rt, cp)
char *cp;
# else
no_output(ifp, m, s, rt)
# endif
struct rtentry *rt;
#endif
struct ifnet *ifp;
struct mbuf *m;
struct sockaddr *s;
{
return 0;
}
static int
#if defined(__sgi) && (IRIX < 60500)
write_output(ifp, m, s)
#else
# if TRU64 >= 1885
write_output (ifp, m, s, rt, cp)
char *cp;
# else
write_output(ifp, m, s, rt)
# endif
struct rtentry *rt;
#endif
struct ifnet *ifp;
struct mbuf *m;
struct sockaddr *s;
{
char fname[32];
mb_t *mb;
ip_t *ip;
int fd;
mb = (mb_t *)m;
ip = MTOD(mb, ip_t *);
#if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \
(defined(OpenBSD) && (OpenBSD >= 199603)) || defined(linux) || \
(defined(__FreeBSD__) && (__FreeBSD_version >= 501113))
sprintf(fname, "/tmp/%s", ifp->if_xname);
#else
sprintf(fname, "/tmp/%s%d", ifp->if_name, ifp->if_unit);
#endif
fd = open(fname, O_WRONLY|O_APPEND);
if (fd == -1) {
perror("open");
return -1;
}
write(fd, (char *)ip, ntohs(ip->ip_len));
close(fd);
return 0;
}
static void
ipf_setifpaddr(ifp, addr)
struct ifnet *ifp;
char *addr;
{
#ifdef __sgi
struct in_ifaddr *ifa;
#else
struct ifaddr *ifa;
#endif
#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
if (ifp->if_addrlist.tqh_first != NULL)
#else
# ifdef __sgi
if (ifp->in_ifaddr != NULL)
# else
if (ifp->if_addrlist != NULL)
# endif
#endif
return;
ifa = (struct ifaddr *)malloc(sizeof(*ifa));
#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
ifp->if_addrlist.tqh_first = ifa;
#else
# ifdef __sgi
ifp->in_ifaddr = ifa;
# else
ifp->if_addrlist = ifa;
# endif
#endif
if (ifa != NULL) {
struct sockaddr_in *sin;
#ifdef __sgi
sin = (struct sockaddr_in *)&ifa->ia_addr;
#else
sin = (struct sockaddr_in *)&ifa->ifa_addr;
#endif
#ifdef USE_INET6
if (index(addr, ':') != NULL) {
struct sockaddr_in6 *sin6;
sin6 = (struct sockaddr_in6 *)&ifa->ifa_addr;
sin6->sin6_family = AF_INET6;
/* Abort if bad address. */
switch (inet_pton(AF_INET6, addr, &sin6->sin6_addr))
{
case 1:
break;
case -1:
perror("inet_pton");
abort();
break;
default:
abort();
break;
}
} else
#endif
{
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = inet_addr(addr);
if (sin->sin_addr.s_addr == 0)
abort();
}
}
}
struct ifnet *
get_unit(name, family)
char *name;
int family;
{
struct ifnet *ifp, **ifpp, **old_ifneta;
char *addr;
#if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \
(defined(OpenBSD) && (OpenBSD >= 199603)) || defined(linux) || \
(defined(__FreeBSD__) && (__FreeBSD_version >= 501113))
if (!*name)
return NULL;
if (name == NULL)
name = "anon0";
addr = strchr(name, '=');
if (addr != NULL)
*addr++ = '\0';
for (ifpp = ifneta; ifpp && (ifp = *ifpp); ifpp++) {
if (!strcmp(name, ifp->if_xname)) {
if (addr != NULL)
ipf_setifpaddr(ifp, addr);
return ifp;
}
}
#else
char *s, ifname[LIFNAMSIZ+1];
if (name == NULL)
name = "anon0";
addr = strchr(name, '=');
if (addr != NULL)
*addr++ = '\0';
for (ifpp = ifneta; ifpp && (ifp = *ifpp); ifpp++) {
COPYIFNAME(family, ifp, ifname);
if (!strcmp(name, ifname)) {
if (addr != NULL)
ipf_setifpaddr(ifp, addr);
return ifp;
}
}
#endif
if (!ifneta) {
ifneta = (struct ifnet **)malloc(sizeof(ifp) * 2);
if (!ifneta)
return NULL;
ifneta[1] = NULL;
ifneta[0] = (struct ifnet *)calloc(1, sizeof(*ifp));
if (!ifneta[0]) {
free(ifneta);
return NULL;
}
nifs = 1;
} else {
old_ifneta = ifneta;
nifs++;
ifneta = (struct ifnet **)reallocarray(ifneta, nifs + 1,
sizeof(ifp));
if (!ifneta) {
free(old_ifneta);
nifs = 0;
return NULL;
}
ifneta[nifs] = NULL;
ifneta[nifs - 1] = (struct ifnet *)malloc(sizeof(*ifp));
if (!ifneta[nifs - 1]) {
nifs--;
return NULL;
}
}
ifp = ifneta[nifs - 1];
#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
TAILQ_INIT(&ifp->if_addrlist);
#endif
#if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \
(defined(OpenBSD) && (OpenBSD >= 199603)) || defined(linux) || \
(defined(__FreeBSD__) && (__FreeBSD_version >= 501113))
(void) strncpy(ifp->if_xname, name, sizeof(ifp->if_xname));
#else
s = name + strlen(name) - 1;
for (; s > name; s--) {
if (!ISDIGIT(*s)) {
s++;
break;
}
}
if ((s > name) && (*s != 0) && ISDIGIT(*s)) {
ifp->if_unit = atoi(s);
ifp->if_name = (char *)malloc(s - name + 1);
(void) strncpy(ifp->if_name, name, s - name);
ifp->if_name[s - name] = '\0';
} else {
ifp->if_name = strdup(name);
ifp->if_unit = -1;
}
#endif
ifp->if_output = (void *)no_output;
if (addr != NULL) {
ipf_setifpaddr(ifp, addr);
}
return ifp;
}
char *
get_ifname(ifp)
struct ifnet *ifp;
{
static char ifname[LIFNAMSIZ];
#if defined(__OpenBSD__) || defined(__NetBSD__) || defined(linux) || \
(defined(__FreeBSD__) && (__FreeBSD_version >= 501113))
sprintf(ifname, "%s", ifp->if_xname);
#else
if (ifp->if_unit != -1)
sprintf(ifname, "%s%d", ifp->if_name, ifp->if_unit);
else
strcpy(ifname, ifp->if_name);
#endif
return ifname;
}
void
init_ifp()
{
struct ifnet *ifp, **ifpp;
char fname[32];
int fd;
#if (defined(NetBSD) && (NetBSD <= 1991011) && (NetBSD >= 199606)) || \
(defined(OpenBSD) && (OpenBSD >= 199603)) || defined(linux) || \
(defined(__FreeBSD__) && (__FreeBSD_version >= 501113))
for (ifpp = ifneta; ifpp && (ifp = *ifpp); ifpp++) {
ifp->if_output = (void *)write_output;
sprintf(fname, "/tmp/%s", ifp->if_xname);
fd = open(fname, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600);
if (fd == -1)
perror("open");
else
close(fd);
}
#else
for (ifpp = ifneta; ifpp && (ifp = *ifpp); ifpp++) {
ifp->if_output = (void *)write_output;
sprintf(fname, "/tmp/%s%d", ifp->if_name, ifp->if_unit);
fd = open(fname, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600);
if (fd == -1)
perror("open");
else
close(fd);
}
#endif
}
int
ipf_fastroute(m, mpp, fin, fdp)
mb_t *m, **mpp;
fr_info_t *fin;
frdest_t *fdp;
{
struct ifnet *ifp;
ip_t *ip = fin->fin_ip;
frdest_t node;
int error = 0;
frentry_t *fr;
void *sifp;
int sout;
sifp = fin->fin_ifp;
sout = fin->fin_out;
fr = fin->fin_fr;
ip->ip_sum = 0;
if (!(fr->fr_flags & FR_KEEPSTATE) && (fdp != NULL) &&
(fdp->fd_type == FRD_DSTLIST)) {
bzero(&node, sizeof(node));
ipf_dstlist_select_node(fin, fdp->fd_ptr, NULL, &node);
fdp = &node;
}
ifp = fdp->fd_ptr;
if (ifp == NULL)
return 0; /* no routing table out here */
if (fin->fin_out == 0) {
fin->fin_ifp = ifp;
fin->fin_out = 1;
(void) ipf_acctpkt(fin, NULL);
fin->fin_fr = NULL;
if (!fr || !(fr->fr_flags & FR_RETMASK)) {
u_32_t pass;
(void) ipf_state_check(fin, &pass);
}
switch (ipf_nat_checkout(fin, NULL))
{
case 0 :
break;
case 1 :
ip->ip_sum = 0;
break;
case -1 :
error = -1;
goto done;
break;
}
}
m->mb_ifp = ifp;
printpacket(fin->fin_out, m);
#if defined(__sgi) && (IRIX < 60500)
(*ifp->if_output)(ifp, (void *)ip, NULL);
# if TRU64 >= 1885
(*ifp->if_output)(ifp, (void *)m, NULL, 0, 0);
# else
(*ifp->if_output)(ifp, (void *)m, NULL, 0);
# endif
#endif
done:
fin->fin_ifp = sifp;
fin->fin_out = sout;
return error;
}
int
ipf_send_reset(fin)
fr_info_t *fin;
{
ipfkverbose("- TCP RST sent\n");
return 0;
}
int
ipf_send_icmp_err(type, fin, dst)
int type;
fr_info_t *fin;
int dst;
{
ipfkverbose("- ICMP unreachable sent\n");
return 0;
}
void
m_freem(m)
mb_t *m;
{
return;
}
void
m_copydata(m, off, len, cp)
mb_t *m;
int off, len;
caddr_t cp;
{
bcopy((char *)m + off, cp, len);
}
int
ipfuiomove(buf, len, rwflag, uio)
caddr_t buf;
int len, rwflag;
struct uio *uio;
{
int left, ioc, num, offset;
struct iovec *io;
char *start;
if (rwflag == UIO_READ) {
left = len;
ioc = 0;
offset = uio->uio_offset;
while ((left > 0) && (ioc < uio->uio_iovcnt)) {
io = uio->uio_iov + ioc;
num = io->iov_len;
if (num > left)
num = left;
start = (char *)io->iov_base + offset;
if (start > (char *)io->iov_base + io->iov_len) {
offset -= io->iov_len;
ioc++;
continue;
}
bcopy(buf, start, num);
uio->uio_resid -= num;
uio->uio_offset += num;
left -= num;
if (left > 0)
ioc++;
}
if (left > 0)
return EFAULT;
}
return 0;
}
u_32_t
ipf_newisn(fin)
fr_info_t *fin;
{
static int iss_seq_off = 0;
u_char hash[16];
u_32_t newiss;
MD5_CTX ctx;
/*
* Compute the base value of the ISS. It is a hash
* of (saddr, sport, daddr, dport, secret).
*/
MD5Init(&ctx);
MD5Update(&ctx, (u_char *) &fin->fin_fi.fi_src,
sizeof(fin->fin_fi.fi_src));
MD5Update(&ctx, (u_char *) &fin->fin_fi.fi_dst,
sizeof(fin->fin_fi.fi_dst));
MD5Update(&ctx, (u_char *) &fin->fin_dat, sizeof(fin->fin_dat));
/* MD5Update(&ctx, ipf_iss_secret, sizeof(ipf_iss_secret)); */
MD5Final(hash, &ctx);
memcpy(&newiss, hash, sizeof(newiss));
/*
* Now increment our "timer", and add it in to
* the computed value.
*
* XXX Use `addin'?
* XXX TCP_ISSINCR too large to use?
*/
iss_seq_off += 0x00010000;
newiss += iss_seq_off;
return newiss;
}
/* ------------------------------------------------------------------------ */
/* Function: ipf_nextipid */
/* Returns: int - 0 == success, -1 == error (packet should be droppped) */
/* Parameters: fin(I) - pointer to packet information */
/* */
/* Returns the next IPv4 ID to use for this packet. */
/* ------------------------------------------------------------------------ */
INLINE u_short
ipf_nextipid(fin)
fr_info_t *fin;
{
static u_short ipid = 0;
ipf_main_softc_t *softc = fin->fin_main_soft;
u_short id;
MUTEX_ENTER(&softc->ipf_rw);
if (fin->fin_pktnum != 0) {
/*
* The -1 is for aligned test results.
*/
id = (fin->fin_pktnum - 1) & 0xffff;
} else {
}
id = ipid++;
MUTEX_EXIT(&softc->ipf_rw);
return id;
}
INLINE int
ipf_checkv4sum(fin)
fr_info_t *fin;
{
if (fin->fin_flx & FI_SHORT)
return 1;
if (ipf_checkl4sum(fin) == -1) {
fin->fin_flx |= FI_BAD;
return -1;
}
return 0;
}
#ifdef USE_INET6
INLINE int
ipf_checkv6sum(fin)
fr_info_t *fin;
{
if (fin->fin_flx & FI_SHORT)
return 1;
if (ipf_checkl4sum(fin) == -1) {
fin->fin_flx |= FI_BAD;
return -1;
}
return 0;
}
#endif
#if 0
/*
* See above for description, except that all addressing is in user space.
*/
int
copyoutptr(softc, src, dst, size)
void *src, *dst;
size_t size;
{
caddr_t ca;
bcopy(dst, (char *)&ca, sizeof(ca));
bcopy(src, ca, size);
return 0;
}
/*
* See above for description, except that all addressing is in user space.
*/
int
copyinptr(src, dst, size)
void *src, *dst;
size_t size;
{
caddr_t ca;
bcopy(src, (char *)&ca, sizeof(ca));
bcopy(ca, dst, size);
return 0;
}
#endif
/*
* return the first IP Address associated with an interface
*/
int
ipf_ifpaddr(softc, v, atype, ifptr, inp, inpmask)
ipf_main_softc_t *softc;
int v, atype;
void *ifptr;
i6addr_t *inp, *inpmask;
{
struct ifnet *ifp = ifptr;
#ifdef __sgi
struct in_ifaddr *ifa;
#else
struct ifaddr *ifa;
#endif
#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD__)
ifa = ifp->if_addrlist.tqh_first;
#else
# ifdef __sgi
ifa = (struct in_ifaddr *)ifp->in_ifaddr;
# else
ifa = ifp->if_addrlist;
# endif
#endif
if (ifa != NULL) {
if (v == 4) {
struct sockaddr_in *sin, mask;
mask.sin_addr.s_addr = 0xffffffff;
#ifdef __sgi
sin = (struct sockaddr_in *)&ifa->ia_addr;
#else
sin = (struct sockaddr_in *)&ifa->ifa_addr;
#endif
return ipf_ifpfillv4addr(atype, sin, &mask,
&inp->in4, &inpmask->in4);
}
#ifdef USE_INET6
if (v == 6) {
struct sockaddr_in6 *sin6, mask;
sin6 = (struct sockaddr_in6 *)&ifa->ifa_addr;
((i6addr_t *)&mask.sin6_addr)->i6[0] = 0xffffffff;
((i6addr_t *)&mask.sin6_addr)->i6[1] = 0xffffffff;
((i6addr_t *)&mask.sin6_addr)->i6[2] = 0xffffffff;
((i6addr_t *)&mask.sin6_addr)->i6[3] = 0xffffffff;
return ipf_ifpfillv6addr(atype, sin6, &mask,
inp, inpmask);
}
#endif
}
return 0;
}
/*
* This function is not meant to be random, rather just produce a
* sequence of numbers that isn't linear to show "randomness".
*/
u_32_t
ipf_random()
{
static unsigned int last = 0xa5a5a5a5;
static int calls = 0;
int number;
calls++;
/*
* These are deliberately chosen to ensure that there is some
* attempt to test whether the output covers the range in test n18.
*/
switch (calls)
{
case 1 :
number = 0;
break;
case 2 :
number = 4;
break;
case 3 :
number = 3999;
break;
case 4 :
number = 4000;
break;
case 5 :
number = 48999;
break;
case 6 :
number = 49000;
break;
default :
number = last;
last *= calls;
last++;
number ^= last;
break;
}
return number;
}
int
ipf_verifysrc(fin)
fr_info_t *fin;
{
return 1;
}
int
ipf_inject(fin, m)
fr_info_t *fin;
mb_t *m;
{
FREE_MB_T(m);
return 0;
}
u_int
ipf_pcksum(fin, hlen, sum)
fr_info_t *fin;
int hlen;
u_int sum;
{
u_short *sp;
u_int sum2;
int slen;
slen = fin->fin_plen - hlen;
sp = (u_short *)((u_char *)fin->fin_ip + hlen);
for (; slen > 1; slen -= 2)
sum += *sp++;
if (slen)
sum += ntohs(*(u_char *)sp << 8);
while (sum > 0xffff)
sum = (sum & 0xffff) + (sum >> 16);
sum2 = (u_short)(~sum & 0xffff);
return sum2;
}
void *
ipf_pullup(m, fin, plen)
mb_t *m;
fr_info_t *fin;
int plen;
{
if (M_LEN(m) >= plen)
return fin->fin_ip;
/*
* Fake ipf_pullup failing
*/
fin->fin_reason = FRB_PULLUP;
*fin->fin_mp = NULL;
fin->fin_m = NULL;
fin->fin_ip = NULL;
return NULL;
}