Network stack kernel modules moved to /current tree.

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@934 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Philippe Houdoin
2002-08-29 08:07:10 +00:00
parent c374d17e22
commit 5de6083d95
35 changed files with 14329 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
SubDir OBOS_TOP src add-ons kernel network ;
SubInclude OBOS_TOP src add-ons kernel network core ;
SubInclude OBOS_TOP src add-ons kernel network interfaces ;
SubInclude OBOS_TOP src add-ons kernel network protocols ;
+2
View File
@@ -0,0 +1,2 @@
SubDir OBOS_TOP src add-ons kernel network core ;
+121
View File
@@ -0,0 +1,121 @@
#include <stdio.h>
#ifdef _KERNEL_
#include <KernelExport.h>
#endif
#include "net_misc.h"
/* This from Stevens Vol.2 */
#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1];ADDCARRY(sum);}
uint16 in_cksum(struct mbuf *m, int len, int off)
{
uint16 *w;
int sum = 0;
int mlen = 0;
int byte_swapped = 0;
struct mbuf *orig_m = m;
union {
uint8 c[2];
uint16 s;
} s_util;
union {
uint16 s[2];
uint32 l;
} l_util;
if (off) {
m->m_len -= off;
m->m_data += off;
if (m->m_flags & M_PKTHDR)
m->m_pkthdr.len -= off;
}
for (; m && len; m=m->m_next) {
if (m->m_len == 0)
continue;
w = mtod(m, uint16 *);
if (mlen == -1) {
/* first byte is a continuation of
* a 16 bit word spanning this mbuf
* and the previous one.
*
* s_util.c[0] is already saved.
*/
s_util.c[1] = *(char*)w;
sum += s_util.s;
w = (uint16*) ((char*) w + 1);
mlen = m->m_len - 1;
len--;
} else
mlen = m->m_len;
if (len < mlen)
mlen = len;
len -= mlen;
/* force to even boundry */
if ((1 & (int)w) && (mlen > 0)) {
REDUCE;
sum <<= 8;
s_util.c[0] = *(char*)w;
w = (uint16*)((char*)w + 1);
mlen--;
byte_swapped = 1;
}
/* unroll the loop to make overhead from branches
* &c small.
*/
while ((mlen -= 32) >= 0) {
sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
w += 16;
}
mlen += 32;
while ((mlen -= 8) >= 0) {
sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
w += 4;
}
mlen += 8;
if (mlen == 0 && byte_swapped == 0)
continue;
REDUCE;
while ((mlen -= 2) >= 0) {
sum += *w++;
}
if (byte_swapped) {
REDUCE;
sum <<= 8;
byte_swapped = 0;
if (mlen == -1) {
s_util.c[1] = *(char*)w;
sum += s_util.s;
mlen = 0;
} else
mlen = -1;
} else if (mlen == -1)
s_util.c[0] = *(char*)w;
}
if (len)
printf("cksum: out of data!\n");
if (mlen == -1) {
/* last mbuf was an odd number of bytes! */
s_util.c[1] = 0;
sum += s_util.s;
}
REDUCE;
if (off) {
orig_m->m_len += off;
orig_m->m_data -= off;
if (orig_m->m_flags & M_PKTHDR)
orig_m->m_pkthdr.len += off;
}
return (uint16)(~sum & 0xffff);
}
File diff suppressed because it is too large Load Diff
+429
View File
@@ -0,0 +1,429 @@
/* if helper functions */
#include <kernel/OS.h>
#include <stdio.h>
#include <string.h>
#ifdef _KERNEL_
#include <KernelExport.h>
#endif
#include "netinet/in.h"
#include "sys/socketvar.h"
#include "net/if.h"
#include "net/if_dl.h"
#include "sys/sockio.h"
#include "netinet/in_var.h"
#include "net/route.h"
#include "sys/protosw.h"
extern struct ifnet *devices;
extern int ndevs;
/* Variables used outside this file */
struct ifaddr **ifnet_addrs;
/* Private variables */
static int if_index;
static int if_indexlim;
struct ifnet *get_interfaces(void)
{
return devices;
}
void if_init(void)
{
ifnet_addrs = NULL;
if_index = 0;
if_indexlim = 8; /* initial value */
}
int ifioctl(struct socket *so, int cmd, caddr_t data)
{
struct ifnet *ifp;
struct ifreq *ifr;
if (cmd == SIOCGIFCONF)
return (ifconf(cmd, data));
ifr = (struct ifreq*) data;
ifp = ifunit(ifr->ifr_name);
if (ifp == NULL)
return ENXIO;
switch(cmd) {
case SIOCGIFFLAGS:
/* get interface flags */
ifr->ifr_flags = ifp->if_flags;
break;
case SIOCGIFMETRIC:
/* get interface metric */
ifr->ifr_metric = ifp->if_metric;
break;
case SIOCGIFMTU:
/* get interface MTU */
ifr->ifr_mtu = ifp->if_mtu;
break;
case SIOCSIFFLAGS:
ifp->if_flags = ifr->ifr_flags;
/* restart card with new settings... */
break;
case SIOCSIFMETRIC:
/* set interface metric */
ifp->if_metric = ifr->ifr_metric;
break;
default:
if (so->so_proto == NULL)
return EOPNOTSUPP;
return (*so->so_proto->pr_userreq)(so, PRU_CONTROL,
(struct mbuf*)cmd, (struct mbuf*)data, (struct mbuf*)ifp);
}
return 0;
}
struct ifnet *ifunit(char *name)
{
ifnet *d = devices;
for (d=devices;d;d = d->if_next)
if (strcmp(d->if_name, name) == 0)
return d;
return NULL;
}
void dump_sockaddr(void *ptr)
{
struct sockaddr *sa = (struct sockaddr *)ptr;
uint8 *d = NULL;
int i;
switch (sa->sa_family) {
case AF_LINK: {
struct sockaddr_dl *sdl = (struct sockaddr_dl *)ptr;
if (sdl->sdl_type == IFT_ETHER) {
printf("\t\tETHERNET: ");
printf("Interface ");
d = (unsigned char *)&sdl->sdl_data[0];
for (i=0;i<sdl->sdl_nlen;i++, d++) {
printf("%c", *d);
}
printf(" -> ");
for (i=0;i<sdl->sdl_alen;i++, d++) {
printf("%02x", *d);
if (i< 5)
printf(":");
}
printf("\n");
}
break;
}
case AF_INET: {
struct sockaddr_in *sin = (struct sockaddr_in *)ptr;
struct in_addr ho;
ho.s_addr = sin->sin_addr.s_addr;
printf("\t\tIPv4: ");
d = (uint8*)&ho.s_addr;
for (i=0;i<4;i++, d++) {
printf("%d", *d);
if (i < 3)
printf(".");
}
printf("\n");
break;
}
default:
printf("Unknown type... %d\n", sa->sa_family);
}
}
void *protocol_address(struct ifnet *ifa, int family)
{
struct ifaddr *a = ifa->if_addrlist;
for (; a != NULL; a = a->ifa_next) {
if (a->ifa_addr->sa_family == family) {
if (family == AF_INET) {
return &((struct sockaddr_in*)a->ifa_addr)->sin_addr;
} else {
return &a->ifa_addr->sa_data;
}
}
}
return NULL;
}
#define equal(a1, a2) \
(memcmp((caddr_t)(a1), (caddr_t)(a2), ((struct sockaddr *)(a1))->sa_len) == 0)
/*
* Find an interface address specific to an interface best matching
* a given address.
*/
struct ifaddr *ifaof_ifpforaddr(struct sockaddr *addr,
struct ifnet *ifp)
{
struct ifaddr *ifa;
char *cp, *cp2, *cp3;
char *cplim;
struct ifaddr *ifa_maybe = 0;
uint af = addr->sa_family;
if (af >= AF_MAX)
return (NULL);
for (ifa = ifp->if_addrlist; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr->sa_family != af)
continue;
ifa_maybe = ifa;
if (ifa->ifa_netmask == 0) {
if (equal(addr, ifa->ifa_addr) ||
(ifa->ifa_dstaddr && equal(addr, ifa->ifa_dstaddr)))
return (ifa);
continue;
}
cp = (char *)addr->sa_data;
cp2 = (char *)ifa->ifa_addr->sa_data;
cp3 = (char *)ifa->ifa_netmask->sa_data;
cplim = ifa->ifa_netmask->sa_len + (char *)ifa->ifa_netmask;
for (; cp3 < cplim; cp3++)
if ((*cp++ ^ *cp2++) & *cp3)
break;
if (cp3 == cplim)
return (ifa);
}
return (ifa_maybe);
}
struct ifaddr *ifa_ifwithdstaddr(struct sockaddr *addr)
{
struct ifnet *ifp;
struct ifaddr *ifa;
for (ifp = devices; ifp != NULL; ifp = ifp->if_next)
if (ifp->if_flags & IFF_POINTOPOINT)
for (ifa = ifp->if_addrlist; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr->sa_family != addr->sa_family ||
ifa->ifa_dstaddr == NULL)
continue;
if (equal(addr, ifa->ifa_dstaddr))
return (ifa);
}
return (NULL);
}
struct ifaddr *ifa_ifwithaddr(struct sockaddr *addr)
{
struct ifnet *ifp;
struct ifaddr *ifa;
for (ifp = devices; ifp != NULL; ifp = ifp->if_next) {
for (ifa = ifp->if_addrlist; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr->sa_family != addr->sa_family)
continue;
if (equal(addr, ifa->ifa_addr))
return (ifa);
if ((ifp->if_flags & IFF_BROADCAST) && ifa->ifa_broadaddr &&
/* IPv6 doesn't have broadcast */
ifa->ifa_broadaddr->sa_len != 0 &&
equal(ifa->ifa_broadaddr, addr))
return (ifa);
}
}
return (NULL);
}
/*
* Find an interface on a specific network. If many, choice
* is most specific found.
*/
struct ifaddr *ifa_ifwithnet(struct sockaddr *addr)
{
struct ifnet *ifp;
struct ifaddr *ifa;
struct ifaddr *ifa_maybe = NULL;
uint af = addr->sa_family;
char *addr_data = (char *)addr->sa_data, *cplim;
if (af == AF_LINK) {
struct sockaddr_dl *sdl = (struct sockaddr_dl *)addr;
if (sdl->sdl_index && sdl->sdl_index <= ndevs)
return (ifnet_addrs[sdl->sdl_index]);
}
for (ifp = devices; ifp != NULL; ifp = ifp->if_next)
for (ifa = ifp->if_addrlist; ifa != NULL; ifa = ifa->ifa_next) {
register char *cp, *cp2, *cp3;
if (ifa->ifa_addr->sa_family != af ||
ifa->ifa_netmask == 0)
next:
continue;
cp = addr_data;
cp2 = (char *)ifa->ifa_addr->sa_data;
cp3 = (char *)ifa->ifa_netmask->sa_data;
cplim = (char *)ifa->ifa_netmask +
ifa->ifa_netmask->sa_len;
while (cp3 < cplim)
if ((*cp++ ^ *cp2++) & *cp3++)
/* want to continue for() loop */
goto next;
if (ifa_maybe == 0 ||
rn_refines((caddr_t)ifa->ifa_netmask,
(caddr_t)ifa_maybe->ifa_netmask))
ifa_maybe = ifa;
}
return (ifa_maybe);
}
/* XXX - we have a memory leak here! When we clean up we need to free the memory
* that is malloc'd here
*/
void if_attach(struct ifnet *ifp)
{
uint socksize, ifasize;
int namelen, masklen;
struct ifnet **p = &devices;
struct sockaddr_dl *sdl;
struct ifaddr *ifa;
char dname[IFNAMSIZ];
if (!ifp)
return;
sprintf(dname, "%s%d", ifp->name, ifp->if_unit);
ifp->if_name = strdup(dname);
while (*p)
p = &((*p)->if_next);
*p = ifp;
ifp->if_index = ++if_index; /* atomic add ? */
/* allocate memory for ifnet_addrs if required... */
if (ifnet_addrs == NULL || if_index >= if_indexlim) {
uint n = (if_indexlim <<= 1) * sizeof(*ifa);
struct ifaddr **q = (struct ifaddr**)malloc(n);
if (ifnet_addrs) {
memcpy((caddr_t)q, (caddr_t)ifnet_addrs, n / 2);
free(ifnet_addrs);
}
ifnet_addrs = q;
}
/* get the unit # as a string */
namelen = strlen(ifp->if_name);
/* memory: we need to allocate enough memory for the following...
* struct ifaddr
* struct sockaddr_dl that will hold the link level address and name
* struct sockaddr_dl that will hold the mask
*/
#define _offsetof(t, m) ((int)((caddr_t)&((t *)0)->m))
masklen = _offsetof(struct sockaddr_dl, sdl_data[0]) + namelen;
socksize = masklen + ifp->if_addrlen;
#define ROUNDUP(a) (1 + (((a) - 1) | (sizeof(int32) -1)))
socksize = ROUNDUP(socksize);
if (socksize < sizeof(*sdl))
socksize = sizeof(*sdl);
ifasize = sizeof(*ifa) + 2 * socksize;
if ((ifa = (struct ifaddr*)malloc(ifasize))) {
memset(ifa, 0, ifasize);
sdl = (struct sockaddr_dl *)(ifa + 1);
sdl->sdl_len = socksize;
sdl->sdl_family = AF_LINK;
memcpy(&sdl->sdl_data, ifp->if_name, namelen);
sdl->sdl_nlen = namelen;
sdl->sdl_index = ifp->if_index;
sdl->sdl_type = ifp->if_type;
ifnet_addrs[if_index - 1] = ifa;
ifa->ifa_ifp = ifp;
ifa->ifa_next = ifp->if_addrlist;
ifp->if_addrlist = ifa;
ifa->ifa_addr = (struct sockaddr*)sdl;
/* now do mask... */
sdl = (struct sockaddr_dl *)(socksize + (caddr_t)sdl);
ifa->ifa_netmask = (struct sockaddr*)sdl;
sdl->sdl_len = masklen;
/* build the mask */
while (namelen != 0)
sdl->sdl_data[--namelen] = 0xff;
}
}
void if_detach(struct ifnet *ifp)
{
struct ifnet **p = &devices, *q;
for (; (*p)->if_next != ifp ; (*p) = (*p)->if_next)
continue;
q = (*p)->if_next->if_next;
(*p)->if_next = q;
}
/* XXX - memcpy used as copyin / copyout not available. I did look
* for the source for them but was unable to find them in the
* code jungle that is OpenBSD and FreeBSD!
*
* copyin / copyout should maybe be added if they'd add a speed improvement as
* they're used in a lot of other places as well
*/
int ifconf(int cmd, caddr_t data)
{
struct ifconf *ifc = (struct ifconf*)data;
struct ifnet *ifp = devices;
struct ifaddr *ifa = NULL;
char *cp, *ep;
struct ifreq ifr, *ifrp;
int space = ifc->ifc_len; /* how big the buffer is */
void *copyptr = NULL;
ifrp = ifc->ifc_req;
ep = ifr.ifr_name + sizeof(ifr.ifr_name) - 2;
for (; space > sizeof(ifr) && ifp; ifp = ifp->if_next) {
strncpy(ifr.ifr_name, ifp->if_name, sizeof(ifr.ifr_name) - 2);
for (cp = ifr.ifr_name;cp < ep && *cp; cp++)
continue;
*cp = '\0';
if ((ifa = ifp->if_addrlist) == NULL) {
memset((caddr_t)&ifr.ifr_addr, 0, sizeof(ifr.ifr_addr));
copyptr = memcpy((caddr_t) ifrp, (caddr_t) &ifr, sizeof(ifr));
if (copyptr == NULL)
break;
space -= sizeof(ifr), ifrp++;
} else {
for (; space > sizeof(ifr) && ifa; ifa = ifa->ifa_next) {
struct sockaddr *sa = ifa->ifa_addr;
if (sa->sa_len <= sizeof(*sa)) {
printf("sa->sa_len = %d compared to %ld, sa->sa_family = %d\n",
sa->sa_len, sizeof(*sa), sa->sa_family);
ifr.ifr_addr = *sa;
copyptr = memcpy((caddr_t)ifrp, (caddr_t)&ifr, sizeof(ifr));
ifrp++;
} else {
space -= sa->sa_len - sizeof(*sa);
if (space < sizeof(ifr))
break;
copyptr = memcpy((caddr_t)ifrp, (caddr_t)&ifr, sizeof(ifr.ifr_name));
if (copyptr != NULL)
copyptr = memcpy((caddr_t)&ifrp->ifr_addr, (caddr_t)sa, sa->sa_len);
ifrp = (struct ifreq*)(sa->sa_len + (caddr_t)&ifrp->ifr_addr);
}
if (copyptr == NULL)
break;
space -= sizeof(ifr);
}
}
}
ifc->ifc_len -= space;
/* Yuck! */
return (copyptr == NULL ? -1 : 0);
}
+53
View File
@@ -0,0 +1,53 @@
/* some misc functions... */
#include <stdio.h>
#include <strings.h>
#ifdef _KERNEL_
#include <KernelExport.h>
#endif
#include "net/if.h"
struct ifq *start_ifq(void)
{
struct ifq *nifq = (struct ifq*)malloc(sizeof(*nifq));
if (!nifq)
return NULL;
memset(nifq, 0, sizeof(*nifq));
nifq->lock = create_sem(1, "ifq_lock");
nifq->pop = create_sem(0, "ifq_pop");
#ifdef _KERNEL_
set_sem_owner(nifq->lock, B_SYSTEM_TEAM);
set_sem_owner(nifq->pop, B_SYSTEM_TEAM);
#endif
if (nifq->lock < B_OK || nifq->pop < B_OK)
return NULL;
nifq->len = 0;
nifq->maxlen = 50;
nifq->head = nifq->tail = NULL;
return nifq;
}
void stop_ifq(struct ifq *q)
{
struct mbuf *m = NULL;
acquire_sem_etc(q->lock, 1, B_CAN_INTERRUPT, 0);
while (q->head) {
m = q->head;
q->head = m->m_nextpkt;
m->m_nextpkt = NULL;
m_freem(m);
}
q->len = 0;
delete_sem(q->pop);
release_sem_etc(q->lock, 1, B_CAN_INTERRUPT);
delete_sem(q->lock);
free(q);
}
+480
View File
@@ -0,0 +1,480 @@
/* in.c */
#include <stdio.h>
#ifdef _KERNEL_
#include <KernelExport.h>
#endif
#include "netinet/in.h"
#include "netinet/in_var.h"
#include "sys/socketvar.h"
#include "net/if.h"
#include "sys/sockio.h"
#include "net/route.h"
extern struct ifnet **ifnet_addrs;
struct in_ifaddr *get_primary_addr(void)
{
return in_ifaddr;
}
/*
* Trim a mask in a sockaddr
*/
void in_socktrim(struct sockaddr_in *ap)
{
char *cplim = (char *) &ap->sin_addr;
char *cp = (char *) (&ap->sin_addr + 1);
ap->sin_len = 0;
while (--cp >= cplim)
if (*cp) {
(ap)->sin_len = cp - (char *) (ap) + 1;
break;
}
}
#define rtinitflags(x) \
((((x)->ia_ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) != 0) \
? RTF_HOST : 0)
/*
* remove a route to prefix ("connected route" in cisco terminology).
* re-installs the route by using another interface address, if there's one
* with the same prefix (otherwise we lose the route mistakenly).
*/
static int in_scrubprefix(struct in_ifaddr *target)
{
struct in_ifaddr *ia;
struct in_addr prefix, mask, p;
int error;
if ((target->ia_flags & IFA_ROUTE) == 0)
return 0;
if (rtinitflags(target))
prefix = target->ia_dstaddr.sin_addr;
else
prefix = target->ia_addr.sin_addr;
mask = target->ia_sockmask.sin_addr;
prefix.s_addr &= mask.s_addr;
for (ia = in_ifaddr; ia; ia = ia->ia_next) {
/* easy one first */
if (mask.s_addr != ia->ia_sockmask.sin_addr.s_addr)
continue;
if (rtinitflags(ia))
p = ia->ia_dstaddr.sin_addr;
else
p = ia->ia_addr.sin_addr;
p.s_addr &= ia->ia_sockmask.sin_addr.s_addr;
if (prefix.s_addr != p.s_addr)
continue;
/*
* if we got a matching prefix route, move IFA_ROUTE to him
*/
if ((ia->ia_flags & IFA_ROUTE) == 0) {
rtinit(&(target->ia_ifa), (int)RTM_DELETE,
rtinitflags(target));
target->ia_flags &= ~IFA_ROUTE;
error = rtinit(&ia->ia_ifa, (int)RTM_ADD,
rtinitflags(ia) | RTF_UP);
if (error == 0)
ia->ia_flags |= IFA_ROUTE;
return error;
}
}
/*
* noone seem to have prefix route. remove it.
*/
rtinit(&(target->ia_ifa), (int)RTM_DELETE, rtinitflags(target));
target->ia_flags &= ~IFA_ROUTE;
return 0;
}
#undef rtinitflags
int in_ifinit(struct ifnet *dev, struct in_ifaddr *ia, struct sockaddr_in *sin,
int scrub)
{
uint32 i = sin->sin_addr.s_addr;
struct sockaddr_in oldsin;
int error;
int flags = RTF_UP;
oldsin = ia->ia_addr;
ia->ia_addr = *sin;
if (dev && dev->ioctl) {
error = (*dev->ioctl)(dev, SIOCSIFADDR, (caddr_t)ia);
if (error) {
ia->ia_addr = oldsin;
return error;
}
}
if (dev->if_type == IFT_ETHER) {
ia->ia_ifa.ifa_flags |= RTF_CLONING;
}
if (scrub) {
ia->ia_ifa.ifa_addr = (struct sockaddr*)&oldsin;
in_scrubprefix(ia);
ia->ia_ifa.ifa_addr = (struct sockaddr*)&ia->ia_addr;
}
if (IN_CLASSA(i))
ia->ia_netmask = IN_CLASSA_NET;
else if (IN_CLASSB(i))
ia->ia_netmask = IN_CLASSB_NET;
else
ia->ia_netmask = IN_CLASSC_NET;
if (ia->ia_subnetmask == 0) {
ia->ia_subnetmask = ia->ia_netmask;
ia->ia_sockmask.sin_addr.s_addr = ia->ia_subnetmask;
} else
ia->ia_netmask &= ia->ia_subnetmask;
ia->ia_net = i & ia->ia_netmask;
ia->ia_subnet = i & ia->ia_subnetmask;
in_socktrim(&ia->ia_sockmask);
ia->ia_ifa.ifa_metric = dev->if_metric;
if (dev->if_flags & IFF_BROADCAST) {
ia->ia_broadaddr.sin_addr.s_addr = ia->ia_subnet | ~ia->ia_subnetmask;
ia->ia_netbroadcast.s_addr = ia->ia_net | ~ia->ia_netmask;
} else if (dev->if_flags & IFF_LOOPBACK) {
ia->ia_ifa.ifa_dstaddr = ia->ia_ifa.ifa_addr;
flags |= RTF_HOST;
} else if (dev->if_flags & IFF_POINTOPOINT) {
if (ia->ia_dstaddr.sin_family != AF_INET)
return 0;
flags |= RTF_HOST;
}
/* This is really useful debugging code, but not needed at the moment... */
#if 0
printf("in_ifaddr:\n");
printf(" : ia_net : %08lx\n", ia->ia_net);
printf(" : ia_netmask : %08lx\n", ia->ia_netmask);
printf(" : ia_subnet : %08lx\n", ia->ia_subnet);
printf(" : ia_subnetmask : %08lx\n", ia->ia_subnetmask);
printf(" : ia_netbroadcast : %08lx\n", ia->ia_netbroadcast.s_addr);
printf(" : ia_addr : %08lx\n", ia->ia_addr.sin_addr.s_addr);
printf(" : ia_dstaddr : %08lx\n", ia->ia_dstaddr.sin_addr.s_addr);
printf(" : ia_sockmask : %08lx\n", ia->ia_sockmask.sin_addr.s_addr);
#endif
error = rtinit(&(ia->ia_ifa), (int)RTM_ADD, flags);
if (error == 0)
ia->ia_flags |= IFA_ROUTE;
/* XXX - Multicast address list */
return error;
}
int in_control(struct socket *so, int cmd, caddr_t data, struct ifnet *ifp)
{
struct ifreq *ifr = (struct ifreq*)data;
struct in_ifaddr *ia = NULL;
struct ifaddr *ifa;
struct in_ifaddr *oia;
struct in_aliasreq *ifra = (struct in_aliasreq*)data;
struct sockaddr_in oldaddr;
int error = 0, hostIsNew, maskIsNew;
long i;
if (ifp) /* we need to find the in_ifaddr */
for (ia = in_ifaddr;ia; ia = ia->ia_next)
if (ia->ia_ifp == ifp)
break;
switch (cmd) {
case SIOCAIFADDR:
printf("SIOCAIFADDR\n");
/* add an address */
case SIOCDIFADDR:
printf("SIODIFADDR\n");
/* delete an address */
if (ifra->ifra_addr.sin_family == AF_INET)
for (oia = ia; ia; ia = ia->ia_next) {
if (ia->ia_ifp == ifp &&
ia->ia_addr.sin_addr.s_addr == ifra->ifra_addr.sin_addr.s_addr)
break;
}
if (cmd == SIOCDIFADDR && ia == NULL)
return EADDRNOTAVAIL;
case SIOCSIFADDR:
printf("SIOCSIFADDR\n");
/* set an address */
case SIOCSIFNETMASK:
printf("SIOCSIFNETMASK #1 (%d)\n", cmd);
/* set a net mask */
case SIOCSIFDSTADDR:
/* set the destination address of a point to point link */
if (!ifp) {
printf("No interface pointer!\n");
return EINVAL;
}
if (ia == NULL) {
oia = (struct in_ifaddr*)malloc(sizeof(struct in_ifaddr));
if (oia == NULL)
return ENOMEM;
memset(oia, 0, sizeof(*oia));
if ((ia = in_ifaddr)) {
/* we've got other structures - add at end */
for (; ia->ia_next; ia = ia->ia_next)
continue;
ia->ia_next = oia;
} else
in_ifaddr = oia;
ia = oia;
if ((ifa = ifp->if_addrlist)) {
for (; ifa->ifa_next; ifa = ifa->ifa_next)
continue;
ifa->ifa_next = (struct ifaddr*)ia;
} else
ifp->if_addrlist = (struct ifaddr*)ia;
ia->ia_ifa.ifa_addr = (struct sockaddr*) &ia->ia_addr;
ia->ia_ifa.ifa_dstaddr = (struct sockaddr*) &ia->ia_dstaddr;
ia->ia_ifa.ifa_netmask = (struct sockaddr*) &ia->ia_sockmask;
ia->ia_sockmask.sin_len = 8;
if (ifp->if_flags & IFF_BROADCAST) {
ia->ia_broadaddr.sin_len = sizeof(ia->ia_addr);
ia->ia_broadaddr.sin_family = AF_INET;
}
ia->ia_ifp = ifp;
}
break;
case SIOCSIFBRDADDR:
case SIOCGIFADDR:
case SIOCGIFNETMASK:
case SIOCGIFDSTADDR:
case SIOCGIFBRDADDR:
if (ia == NULL)
return EADDRNOTAVAIL;
break;
}
printf("loop #2 : %d [%ld]\n", cmd, SIOCSIFNETMASK);
switch(cmd) {
case SIOCGIFADDR:
/* get interface address */
*((struct sockaddr_in*) &ifr->ifr_addr) = ia->ia_addr;
break;
case SIOCGIFDSTADDR:
/* get interface point to point destination address */
if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
/* we're not a point to point interface */
return EINVAL;
*((struct sockaddr_in*) &ifr->ifr_dstaddr) = ia->ia_dstaddr;
break;
case SIOCGIFBRDADDR:
/* get interface broadcast address */
if ((ifp->if_flags & IFF_BROADCAST) == 0)
/* we're not a broadcast capable interface */
return EINVAL;
*((struct sockaddr_in*) &ifr->ifr_dstaddr) = ia->ia_broadaddr;
break;
case SIOCGIFNETMASK:
/* get interface netmask */
*((struct sockaddr_in*) &ifr->ifr_addr) = ia->ia_sockmask;
break;
case SIOCSIFADDR:
printf("SIOCSIFADDR #2\n");
return in_ifinit(ifp, ia, (struct sockaddr_in*)&ifr->ifr_addr, 1);
case SIOCSIFNETMASK:
printf("Setting netmask\n");
/* set the netmask for the interface... */
/* set i to the network netmask (network host order) */
i = ifra->ifra_addr.sin_addr.s_addr;
/* set the host byte order netmask into ia_subnetmask */
ia->ia_subnetmask = ntohl((ia->ia_sockmask.sin_addr.s_addr = i));
printf("ia->ia_subnetmask: %08lx\n", ia->ia_subnetmask);
break;
case SIOCSIFDSTADDR:
if ((ifp->if_flags & IFF_POINTOPOINT) == 0)
return EINVAL;
oldaddr = ia->ia_dstaddr;
ia->ia_dstaddr = *(struct sockaddr_in*)&ifr->ifr_dstaddr;
/* update the interface if required */
if (ifp->ioctl) {
error = ifp->ioctl(ifp, SIOCSIFDSTADDR, (caddr_t) ia);
if (error) {
ia->ia_dstaddr = oldaddr;
return error;
}
}
/* change the routing info if it's been set */
if (ia->ia_flags & IFA_ROUTE) {
ia->ia_ifa.ifa_dstaddr = (struct sockaddr*)&oldaddr;
rtinit(&(ia->ia_ifa), RTM_DELETE, RTF_HOST);
ia->ia_ifa.ifa_dstaddr = (struct sockaddr*)&ia->ia_dstaddr;
rtinit(&(ia->ia_ifa), RTM_ADD, RTF_HOST|RTF_UP);
}
break;
case SIOCSIFBRDADDR:
/* set the broadcast address if interface supports it */
if ((ifp->if_flags & IFF_BROADCAST) == 0)
/* we don't support broadcast on that interface */
return EINVAL;
ia->ia_broadaddr = *(struct sockaddr_in*) &ifr->ifr_broadaddr;
break;
case SIOCAIFADDR:
maskIsNew = 0;
hostIsNew = 1;
error = 0;
if (ia->ia_addr.sin_family == AF_INET) {
if (ifra->ifra_addr.sin_len == 0) {
ifra->ifra_addr = ia->ia_addr;
hostIsNew = 0;
} else if (ifra->ifra_addr.sin_addr.s_addr ==
ia->ia_addr.sin_addr.s_addr)
hostIsNew = 0;
}
if (ifra->ifra_mask.sin_len) {
in_scrubprefix(ia);
ia->ia_sockmask = ifra->ifra_mask;
ia->ia_subnetmask = ia->ia_sockmask.sin_addr.s_addr;
maskIsNew = 1;
}
if ((ifp->if_flags & IFF_POINTOPOINT) &&
(ifra->ifra_dstaddr.sin_family == AF_INET)) {
in_scrubprefix(ia);
ia->ia_dstaddr = ifra->ifra_dstaddr;
maskIsNew = 1;
}
if (ifra->ifra_addr.sin_family == AF_INET &&
(hostIsNew || maskIsNew))
error = in_ifinit(ifp, ia, &ifra->ifra_addr, 0);
if ((ifp->if_flags & IFF_BROADCAST) &&
(ifra->ifra_broadaddr.sin_family == AF_INET))
ia->ia_broadaddr = ifra->ifra_broadaddr;
return error;
default:
printf("2nd iteration: default (%d)\n", cmd);
/* if we don't have enough to do the default, return */
if (ifp == NULL || ifp->ioctl == NULL)
return EINVAL; /* XXX - should be EOPNOTSUPP */
/* send to the card and let it process it */
return ifp->ioctl(ifp, cmd, data);
}
return 0;
}
/*
* Return 1 if the address might be a local broadcast address.
*/
int in_broadcast(struct in_addr in, struct ifnet *ifp)
{
struct ifnet *ifn, *if_first, *if_target;
struct ifaddr *ifa;
if (in.s_addr == INADDR_BROADCAST ||
in.s_addr == INADDR_ANY)
return 1;
if (ifp && ((ifp->if_flags & IFF_BROADCAST) == 0))
return 0;
if (ifp == NULL) {
if_first = *ifnet_addrs;
if_target = 0;
} else {
if_first = ifp;
if_target = ifp->if_next;
}
#define ia (ifatoia(ifa))
/*
* Look through the list of addresses for a match
* with a broadcast address.
* If ifp is NULL, check against all the interfaces.
*/
for (ifn = if_first; ifn != if_target; ifn = ifn->if_next) {
for (ifa = ifn->if_addrlist; ifa; ifa = ifa->ifa_next) {
if (!ifp) {
if (ifa->ifa_addr->sa_family == AF_INET &&
((ia->ia_subnetmask != 0xffffffff &&
(((ifn->if_flags & IFF_BROADCAST) &&
in.s_addr == ia->ia_broadaddr.sin_addr.s_addr) ||
in.s_addr == ia->ia_subnet)) ||
/*
* Check for old-style (host 0) broadcast.
*/
(in.s_addr == ia->ia_netbroadcast.s_addr ||
in.s_addr == ia->ia_net)))
return 1;
else
if (ifa->ifa_addr->sa_family == AF_INET &&
(((ifn->if_flags & IFF_BROADCAST) &&
in.s_addr == ia->ia_broadaddr.sin_addr.s_addr) ||
in.s_addr == ia->ia_netbroadcast.s_addr ||
/*
* Check for old-style (host 0) broadcast.
*/
in.s_addr == ia->ia_subnet ||
in.s_addr == ia->ia_net))
return 1;
}
}
}
return (0);
#undef ia
}
#ifndef SUBNETSARELOCAL
#define SUBNETSARELOCAL 0
#endif
int subnetsarelocal = SUBNETSARELOCAL;
/*
* Return 1 if an internet address is for a ``local'' host
* (one to which we have a connection). If subnetsarelocal
* is true, this includes other subnets of the local net.
* Otherwise, it includes only the directly-connected (sub)nets.
*/
int in_localaddr(struct in_addr in)
{
struct in_ifaddr *ia;
if (subnetsarelocal) {
for (ia = in_ifaddr; ia != 0; ia = ia->ia_next)
if ((in.s_addr & ia->ia_netmask) == ia->ia_net)
return (1);
} else {
for (ia = in_ifaddr; ia != 0; ia = ia->ia_next)
if ((in.s_addr & ia->ia_subnetmask) == ia->ia_subnet)
return (1);
}
return (0);
}
int in_canforward(struct in_addr in)
{
uint32 i = ntohl(in.s_addr);
uint32 net;
if (IN_EXPERIMENTAL(i) || IN_MULTICAST(i))
return 0;
if (IN_CLASSA(i)) {
net = i & IN_CLASSA_NET;
if (net == 0 || net == (IN_LOOPBACKNET << IN_CLASSA_NSHIFT))
return 0;
}
return 1;
}
+464
View File
@@ -0,0 +1,464 @@
/* inpcb.c
*
* implementation of internet control blocks code
*/
#include <stdio.h>
#ifdef _KERNEL_
#include <KernelExport.h>
#endif
#include "pools.h"
#include "sys/socketvar.h"
#include "netinet/in.h"
#include "netinet/in_pcb.h"
#include "net/if.h"
#include "netinet/in_var.h"
#include "sys/protosw.h"
static struct pool_ctl *pcbpool = NULL;
static struct in_addr zeroin_addr;
int inetctlerrmap[PRC_NCMDS] = {
0, 0, 0, 0,
0, EMSGSIZE, EHOSTDOWN, EHOSTUNREACH,
EHOSTUNREACH, EHOSTUNREACH, ECONNREFUSED, ECONNREFUSED,
EMSGSIZE, EHOSTUNREACH, 0, 0,
0, 0, 0, 0,
ENOPROTOOPT
};
int inpcb_init(void)
{
in_ifaddr = NULL;
if (!pcbpool)
pool_init(&pcbpool, sizeof(struct inpcb));
if (!pcbpool) {
printf("inpcb_init: ENOMEM\n");
return ENOMEM;
}
zeroin_addr.s_addr = 0;
return 0;
}
int in_pcballoc(struct socket *so, struct inpcb *head)
{
struct inpcb *inp;
inp = (struct inpcb *)pool_get(pcbpool);
if (!inp) {
printf("in_pcballoc: ENOMEM\n");
return ENOMEM;
}
memset(inp, 0, sizeof(*inp));
inp->inp_head = head;
/* associate ourselves with the socket */
inp->inp_socket = so;
insque(inp, head);
so->so_pcb = (caddr_t)inp;
return 0;
}
void in_pcbdetach(struct inpcb *inp)
{
struct socket *so = inp->inp_socket;
so->so_pcb = NULL;
/* BSD sockets would call sofree here - we can't.
* The first thing that sofree does in BSD is check whether
* there are still file system references to the socket
* (SS_NOFDREF) and if there are it doesn't free. We don't have
* the same relationship to our sockets, as we use the socket for
* the kernel cookie, and freeing it here would lead to real problems,
* so we leave the socket until we call soclose()
* This may need to be reviewed and an extra layer of abstraction
* added at some point if we find it's using too much system resource.
*/
if (inp->inp_options)
m_free(inp->inp_options);
if (inp->inp_route.ro_rt)
rtfree(inp->inp_route.ro_rt);
remque(inp);
pool_put(pcbpool, inp);
}
int in_pcbbind(struct inpcb *inp, struct mbuf *nam)
{
struct socket *so = inp->inp_socket;
struct inpcb *head = inp->inp_head;
struct sockaddr_in *sin;
uint16 lport = 0;
int wild = 0;
int reuseport = (so->so_options & SO_REUSEPORT);
if (inp->lport || inp->laddr.s_addr != INADDR_ANY) {
printf("in_pcbbind: EINVAL (%08lx:%d)\n", inp->laddr.s_addr, inp->lport);
return EINVAL;
}
/* XXX - yuck! Try to format this better */
/* This basically checks all the options that might be set that allow
* us to use wildcard searches.
*/
if (((so->so_options & (SO_REUSEADDR | SO_REUSEPORT)) == 0) &&
((so->so_proto->pr_flags & PR_CONNREQUIRED) == 0 ||
(so->so_options & SO_ACCEPTCONN) == 0))
wild = INPLOOKUP_WILDCARD;
if (nam) {
sin = mtod(nam, struct sockaddr_in *);
if (nam->m_len != sizeof(*sin)) {
printf("in_pcbind: EINVAL (m_len = %ld vs %ld)\n",
nam->m_len, sizeof(*sin));
/* whoops, too much data! */
return EINVAL;
}
/* Apparently this may not be correctly
* in older programs, so this may need
* to be commented out...
*/
if (sin->sin_family != AF_INET) {
printf("in_pcbbind: EAFNOSUPPORT\n");
return EAFNOSUPPORT;
}
lport = sin->sin_port;
if (IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
/* need special case for multicast. We'll
* allow the complete binding to be duplicated if
* SO_REUSEPORT is set, or if we have
* SO_REUSEADDR set and both sockets have
* a multicast address bound.
* We'll exit with the reuseport variable set
* correctly.
*/
if (so->so_options & SO_REUSEADDR)
reuseport = SO_REUSEADDR | SO_REUSEPORT;
} else if (sin->sin_addr.s_addr != INADDR_ANY) {
sin->sin_port = 0; /* must be zero for next step */
if (ifa_ifwithaddr((struct sockaddr*)sin) == NULL) {
printf("in_pcbbind: EADDRNOTAVAIL\n");
return EADDRNOTAVAIL;
}
}
if (lport) {
struct inpcb *t;
/* we have something to work with... */
/* XXX - reserved ports have no meaning for us */
/* XXX - fix me if we ever have multi-user */
t = in_pcblookup(head, zeroin_addr, 0,
sin->sin_addr, lport, wild);
if (t && (reuseport & t->inp_socket->so_options) == 0) {
printf("in_pcbbind: EADDRINUSE\n");
return EADDRINUSE;
}
}
inp->laddr = sin->sin_addr;
}
/* if we have an ephemereal port, find a suitable port to use */
if (lport == 0) {
/* ephemereal port!! */
do {
if (head->lport++ < IPPORT_RESERVED ||
head->lport > IPPORT_USERRESERVED) {
head->lport = IPPORT_RESERVED;
}
lport = htons(head->lport);
} while (in_pcblookup(head, zeroin_addr, 0,
inp->laddr, lport, wild));
}
inp->lport = lport;
return 0;
}
struct inpcb *in_pcblookup(struct inpcb *head, struct in_addr faddr,
uint16 fport_a, struct in_addr laddr,
uint16 lport_a, int flags)
{
struct inpcb *inp;
struct inpcb *match = NULL;
int matchwild = 3;
int wildcard;
uint16 fport = fport_a;
uint16 lport = lport_a;
for (inp = head->inp_next; inp != head; inp = inp->inp_next) {
if (inp->lport != lport)
continue; /* local ports don't match */
wildcard = 0;
/* Here we try to find the best match. wildcard is set to 0
* and bumped by one every time we find something that doesn't match
* so we can have a suitable match at the end
*/
if (inp->laddr.s_addr != INADDR_ANY) {
if (laddr.s_addr == INADDR_ANY)
wildcard++;
else if (inp->laddr.s_addr != laddr.s_addr)
continue;
} else {
if (laddr.s_addr != INADDR_ANY)
wildcard++;
}
if (inp->faddr.s_addr != INADDR_ANY) {
if (faddr.s_addr == INADDR_ANY)
wildcard++;
else if (inp->faddr.s_addr != faddr.s_addr ||
inp->fport != fport)
continue;
} else {
if (faddr.s_addr != INADDR_ANY)
wildcard++;
}
if (wildcard && ((flags & INPLOOKUP_WILDCARD) == 0)) {
continue; /* wildcard match is not allowed!! */
}
if (wildcard < matchwild) {
match = inp;
matchwild = wildcard;
if (matchwild == 0)
break; /* exact match!! */
}
}
return match;
}
int in_pcbconnect(struct inpcb *inp, struct mbuf *nam)
{
struct in_ifaddr *ia = NULL;
struct sockaddr_in *ifaddr = NULL;
struct sockaddr_in *sin = mtod(nam, struct sockaddr_in *);
if (nam->m_len != sizeof(*sin)) {
printf("in_pcbconnect: EINVAL\n");
return EINVAL;
}
if (sin->sin_family != AF_INET) {
printf("in_pcbconnect: EAFNOSUPPORT (sin_family = %d, not %d)\n",
sin->sin_family, AF_INET);
return EAFNOSUPPORT;
}
if (sin->sin_port == 0) {
printf("in_pcbconnect: EADDRNOTAVAIL\n");
return EADDRNOTAVAIL;
}
if (in_ifaddr) {
if (sin->sin_addr.s_addr == INADDR_ANY)
sin->sin_addr = IA_SIN(in_ifaddr)->sin_addr;
/* we need to handle INADDR_BROADCAST here as well */
}
if (inp->laddr.s_addr == INADDR_ANY) {
struct route *ro;
ro = &inp->inp_route;
if (ro && ro->ro_rt &&
(satosin(&ro->ro_dst)->sin_addr.s_addr != sin->sin_addr.s_addr
|| inp->inp_socket->so_options & SO_DONTROUTE)) {
RTFREE(ro->ro_rt);
ro->ro_rt = NULL;
}
if ((inp->inp_socket->so_options & SO_DONTROUTE) == 0
&& (ro->ro_rt == NULL
|| ro->ro_rt->rt_ifp == NULL)) {
/* we don't have a route, try to get one */
memset(&ro->ro_dst, 0, sizeof(ro->ro_dst));
ro->ro_dst.sa_family = AF_INET;
ro->ro_dst.sa_len = sizeof(struct sockaddr_in);
((struct sockaddr_in*)&ro->ro_dst)->sin_addr = sin->sin_addr;
rtalloc(ro);
}
/* did we find a route?? */
if (ro->ro_rt && (ro->ro_rt->rt_ifp->if_flags & IFF_LOOPBACK))
ia = ifatoia(ro->ro_rt->rt_ifa);
if (ia == NULL) {
uint16 fport = sin->sin_port;
sin->sin_port = 0;
ia = ifatoia(ifa_ifwithdstaddr(sintosa(sin)));
if (ia == NULL)
ia = ifatoia(ifa_ifwithnet(sintosa(sin)));
sin->sin_port = fport;
if (ia == NULL)
ia = in_ifaddr;
if (ia == NULL) {
printf("in_pcbconnect: EADDRNOTAVAIL\n");
return EADDRNOTAVAIL;
}
}
/* XXX - handle multicast */
ifaddr = (struct sockaddr_in*) &ia->ia_addr;
}
if (in_pcblookup(inp->inp_head, sin->sin_addr, sin->sin_port,
inp->laddr.s_addr ? inp->laddr : ifaddr->sin_addr,
inp->lport, 0)) {
printf("in_pcbconnect: EADDRINUSE\n");
return EADDRINUSE;
}
if (inp->laddr.s_addr == INADDR_ANY) {
if (inp->lport == 0)
in_pcbbind(inp, NULL);
inp->laddr = ifaddr->sin_addr;
}
inp->faddr = sin->sin_addr;
inp->fport = sin->sin_port;
return 0;
}
/* XXX - why is this an int? */
int in_pcbdisconnect(struct inpcb *inp)
{
inp->faddr.s_addr = INADDR_ANY;
inp->fport = 0;
if (inp->inp_socket->so_state & SS_NOFDREF)
in_pcbdetach(inp);
return 0;
}
void in_losing(struct inpcb *inp)
{
struct rtentry *rt;
struct rt_addrinfo info;
if ((rt = inp->inp_route.ro_rt)) {
inp->inp_route.ro_rt = NULL;
memset(&info, 0, sizeof(info));
info.rti_info[RTAX_DST] = (struct sockaddr*)&inp->inp_route.ro_dst;
info.rti_info[RTAX_GATEWAY] = rt->rt_gateway;
info.rti_info[RTAX_NETMASK] = rt_mask(rt);
//rt_missmsg
if (rt->rt_flags & RTF_DYNAMIC)
rtrequest(RTM_DELETE, rt_key(rt), rt->rt_gateway, rt_mask(rt),
rt->rt_flags, NULL);
else
rtfree(rt);
}
}
struct rtentry *in_pcbrtentry(struct inpcb *inp)
{
struct route *ro;
ro = &inp->inp_route;
/*
* No route yet, so try to acquire one.
*/
if (ro->ro_rt == NULL) {
memset(ro, 0, sizeof(struct route));
if (inp->faddr.s_addr != INADDR_ANY) {
/* this probably isn't needed, but better safe than sorry */
memset(&ro->ro_dst, 0, sizeof(ro->ro_dst));
ro->ro_dst.sa_family = AF_INET;
ro->ro_dst.sa_len = sizeof(ro->ro_dst);
satosin(&ro->ro_dst)->sin_addr = inp->faddr;
rtalloc(ro);
}
}
return (ro->ro_rt);
}
int inetctlerr(int cmd)
{
return inetctlerrmap[cmd];
}
/* remove the route associated with a control block (if there is one)
* forcing the route to be allocated next time it's used
*/
static void in_rtchange(struct inpcb *inp, int err)
{
if (inp->inp_route.ro_rt) {
rtfree(inp->inp_route.ro_rt);
inp->inp_route.ro_rt = NULL;
}
}
void in_pcbnotify(struct inpcb *head, struct sockaddr *dst,
uint16 fport_arg, struct in_addr laddr,
uint16 lport_arg, int cmd,
void (*notify)(struct inpcb *, int))
{
struct inpcb *inp, *oinp;
struct in_addr faddr;
uint16 fport = fport_arg, lport = lport_arg;
int err = 0;
if ((uint)cmd > PRC_NCMDS || dst->sa_family != AF_INET)
return;
faddr = satosin(dst)->sin_addr;
if (faddr.s_addr == INADDR_ANY)
return;
if (PRC_IS_REDIRECT(cmd) || cmd == PRC_HOSTDEAD) {
fport = lport = 0;
laddr.s_addr = 0;
if (cmd != PRC_HOSTDEAD)
notify = in_rtchange;
}
err = inetctlerrmap[cmd];
for (inp = head->inp_next; inp != head;) {
if (inp->faddr.s_addr != faddr.s_addr ||
inp->inp_socket == NULL ||
inp->fport != fport ||
inp->lport != lport ||
(laddr.s_addr && inp->laddr.s_addr != laddr.s_addr)) {
inp = inp->inp_next;
continue;
}
oinp = inp;
inp = inp->inp_next;
if (notify)
(*notify)(oinp, err);
}
}
void in_setsockaddr(struct inpcb *inp, struct mbuf *nam)
{
struct sockaddr_in *sin;
nam->m_len = sizeof(*sin);
sin = mtod(nam, struct sockaddr_in *);
memset(sin, 0, sizeof(*sin));
sin->sin_family = AF_INET;
sin->sin_len = sizeof(*sin);
sin->sin_port = inp->lport;
sin->sin_addr = inp->laddr;
}
void in_setpeeraddr(struct inpcb *inp, struct mbuf *nam)
{
struct sockaddr_in *sin;
nam->m_len = sizeof(*sin);
sin = mtod(nam, struct sockaddr_in *);
memset(sin, 0, sizeof(*sin));
sin->sin_family = AF_INET;
sin->sin_len = sizeof(*sin);
sin->sin_port = inp->fport;
sin->sin_addr = inp->faddr;
}
+522
View File
@@ -0,0 +1,522 @@
/* mbuf.c
* network buffer implementation
*/
#ifndef _KERNEL_
#include <stdio.h>
#endif
#include <stdlib.h>
#include <kernel/OS.h>
#include <string.h>
#include "net_misc.h"
#include "pools.h"
#ifdef _KERNEL_
#include <KernelExport.h>
#endif
#define MBUF_ALLOCSIZE 4096
void dump_freelist(void)
{
pool_debug_walk(mbpool);
}
/* init the mbuf data structures */
void mbinit(void)
{
if (!mbpool)
pool_init(&mbpool, sizeof(struct mbuf));
if (!clpool)
pool_init(&clpool, MCLBYTES);
/* XXX - move me to the protocol init routines! */
max_linkhdr = 14;
max_protohdr = 40;
max_hdr = max_linkhdr + max_protohdr;
}
struct mbuf *m_get(int type)
{
struct mbuf *mnew;
MGET(mnew, type);
return mnew;
}
struct mbuf *m_getclr(int type)
{
struct mbuf *mnew;
MGET(mnew, type);
if (!mnew)
return NULL;
memset(mtod(mnew, char *), 0, MLEN);
return mnew;
}
struct mbuf *m_gethdr(int type)
{
struct mbuf *mnew;
MGETHDR(mnew, type);
return mnew;
}
struct mbuf *m_free(struct mbuf *mfree)
{
struct mbuf *succ; /* successor if there is one! */
MFREE(mfree, succ);
return succ;
}
/* Free the entire chain */
void m_freem(struct mbuf *m)
{
struct mbuf *n = NULL;
if (!m)
return;
do {
MFREE(m, n);
//printf("m_freem(%p, %p)\n", m, n);
} while ((m = n) != NULL);
}
struct mbuf *m_prepend(struct mbuf *m, int len)
{
struct mbuf *mnew;
if (M_LEADINGSPACE(m) >= len) {
m->m_data -= len;
m->m_len += len;
} else {
MGET(mnew, m->m_type);
if (!mnew) {
/* free chain */
return NULL;
}
if (m->m_flags & M_PKTHDR)
M_MOVE_PKTHDR(mnew, m);
mnew->m_next = m;
m = mnew;
if (len < MHLEN)
MH_ALIGN(m, len);
m->m_len = len;
}
if (m && m->m_flags & M_PKTHDR)
m->m_pkthdr.len += len;
return m;
}
struct mbuf *m_devget(char *buf, int totlen, int off0,
struct ifnet *ifp,
void (*copy)(const void *, void *, size_t))
{
struct mbuf *m;
struct mbuf *top = NULL, **mp = &top;
int off = off0, len;
char *cp;
char *epkt;
cp = buf;
epkt = cp + totlen;
if (off) {
/*
* If 'off' is non-zero, packet is trailer-encapsulated,
* so we have to skip the type and length fields.
*/
cp += off + 2 * sizeof(uint16);
totlen -= 2 * sizeof(uint16);
}
MGETHDR(m, MT_DATA);
if (m == NULL)
return (NULL);
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = totlen;
m->m_len = MHLEN;
while (totlen > 0) {
if (top != NULL) {
MGET(m, MT_DATA);
if (m == NULL) {
m_freem(top);
return (NULL);
}
m->m_len = MLEN;
}
len = min(totlen, epkt - cp);
if (len >= MINCLSIZE) {
MCLGET(m);
if (m->m_flags & M_EXT)
m->m_len = len = min(len, MCLBYTES);
else
len = m->m_len;
} else {
/*
* Place initial small packet/header at end of mbuf.
*/
if (len < m->m_len) {
if (top == NULL &&
len + max_linkhdr <= m->m_len)
m->m_data += max_linkhdr;
m->m_len = len;
} else
len = m->m_len;
}
if (copy)
copy(cp, mtod(m, void *), (size_t)len);
else
memmove(mtod(m, void *), cp, (size_t)len);
cp += len;
*mp = m;
mp = &m->m_next;
totlen -= len;
if (cp == epkt)
cp = buf;
}
return (top);
}
void m_reserve(struct mbuf *mp, int len)
{
if (mp->m_len == 0) {
/* empty buffer! */
if (mp->m_flags & M_PKTHDR) {
if (len < MHLEN) {
mp->m_data += len;
mp->m_len -= len;
mp->m_pkthdr.len -= len;
return;
}
/* ?? */
} else {
if (len <= MLEN) {
mp->m_data += len;
mp->m_len -= len;
return;
}
}
}
if (len > 0) {
if (len <= mp->m_len) {
mp->m_data += len;
mp->m_len -= len;
}
}
if (mp->m_flags & M_PKTHDR)
mp->m_pkthdr.len -= len;
}
void m_cat(struct mbuf *m, struct mbuf *n)
{
while (m->m_next)
m = m->m_next;
while (n) {
if (m->m_flags & M_EXT ||
m->m_data + m->m_len + n->m_len >= &m->m_dat[MLEN]) {
/* just join them :) */
m->m_next = n;
return;
}
memcpy((void*)(mtod(m, char *) + m->m_len), mtod(n, void*), n->m_len);
m->m_len += n->m_len;
n = m_free(n);
}
}
void m_adj(struct mbuf *mp, int req_len)
{
struct mbuf *m;
int len = req_len, count = 0;
if ((m = mp) == NULL)
return;
if (len >= 0) {
/* trim from the head */
while (m!= NULL && len > 0) {
if (m->m_len <= len) {
/* this whole mbuf isn't enough... */
len -= m->m_len;
m->m_len = 0;
m = m->m_next;
} else {
/* this mbuf just needs trimming */
m->m_len -= len;
m->m_data += len;
len = 0;
}
}
m = mp;
if (mp->m_flags & M_PKTHDR)
m->m_pkthdr.len -= (req_len - len);
} else {
/* trim from tail... */
len = -len;
count = 0;
for (;;) {
count += m->m_len;
if (m->m_next == NULL)
break;
m = m->m_next;
}
if (m->m_len >= len) {
m->m_len -= len;
if (mp->m_flags & M_PKTHDR)
mp->m_pkthdr.len -= len;
return;
}
count -= len;
if (count < 0)
count = 0;
/* The correct length for the chain is now "count".
* find the last mbuf, adjust it's length and toss
* remaining mbufs...
*/
m = mp; /* first mbuf */
if (m->m_flags & M_PKTHDR)
m->m_pkthdr.len = count;
for (; m; m= m->m_next) {
if (m->m_len >= count) {
m->m_len = count;
break;
}
count -= m->m_len;
}
while (m->m_next)
(m = m->m_next)->m_len = 0;
}
}
void m_copydata(struct mbuf *m, int off, int len, caddr_t cp)
{
uint count = 0;
if (off < 0) {
printf("m_copydata: off %d < 0", off);
return;
}
if (len < 0) {
printf("m_copydata: len %d < 0", len);
return;
}
while (off > 0) {
if (m == NULL) {
printf("m_copydata: null mbuf in skip");
return;
}
if (off < m->m_len)
break;
off -= m->m_len;
m = m->m_next;
}
while (len > 0) {
if (m == NULL) {
printf("m_copydata: null mbuf");
return;
}
count = min(m->m_len - off, len);
memcpy(cp, (void*)(mtod(m, char *) + off), count);
len -= count;
cp += count;
off = 0;
m = m->m_next;
}
}
struct mbuf *m_copym(struct mbuf *m, int off0, int len)
{
struct mbuf *n, **np;
int off = off0;
struct mbuf *top;
int copyhdr = 0;
if (off < 0 || len < 0) {
printf("PANIC: m_copym: m: off %d, len %d\n", off, len);
return NULL;
}
if (off == 0 && m->m_flags & M_PKTHDR)
copyhdr = 1;
while (off > 0) {
if (!m) {
printf("PANIC: m_copym: null mbuf\n");
return NULL;
}
if (off < m->m_len)
break;
off -= m->m_len;
m = m->m_next;
}
np = &top;
top = NULL;
while (len > 0) {
if (!m) {
if (len != M_COPYALL) {
printf("PANIC: m_copym: m == NULL and not COPYALL\n");
return NULL;
}
break;
}
MGET(n, m->m_type);
*np = n;
if (!n)
goto nospace;
if (copyhdr) {
M_DUP_PKTHDR(n, m);
if (len == M_COPYALL)
n->m_pkthdr.len -= off0;
else
n->m_pkthdr.len = len;
copyhdr = 0;
}
n->m_len = min(len, m->m_len - off);
if (m->m_flags & M_EXT) {
/*
* we are unsure about the way m was allocated.
* copy into multiple MCLBYTES cluster mbufs.
*/
MCLGET(n);
n->m_len = 0;
n->m_len = M_TRAILINGSPACE(n);
n->m_len = min(n->m_len, len);
n->m_len = min(n->m_len, m->m_len - off);
memcpy(mtod(n, caddr_t), (void *)(mtod(m, char *) + off),
(unsigned)n->m_len);
} else
memcpy(mtod(n, caddr_t), (void*)(mtod(m, char *)+off),
(unsigned)n->m_len);
if (len != M_COPYALL)
len -= n->m_len;
off += n->m_len;
if (off == m->m_len) {
m = m->m_next;
off = 0;
}
np = &n->m_next;
}
return (top);
nospace:
m_freem(top);
return (NULL);
}
/* Rearrange an mbuf chain so that len bytes are contiguous
* and in the data area of an mbuf (so that mtod and dtom
* will work for a structure of size len). Returns the resulting
* mbuf chain on success, frees it and returns null on failure.
* If there is room, it will add up to max_protohdr-len extra bytes to the
* contiguous region in an attempt to avoid being called next time.
*/
int MPFail = 0;
struct mbuf *m_pullup(struct mbuf *n, int len)
{
struct mbuf *m;
int count;
int space;
if (n->m_len <= len)
return n;
/*
* If first mbuf has no cluster, and has room for len bytes
* without shifting current data, pullup into it,
* otherwise allocate a new mbuf to prepend to the chain.
*/
if ((n->m_flags & M_EXT) == 0 &&
n->m_data + len < &n->m_dat[MLEN] && n->m_next) {
if (n->m_len >= len)
return (n);
m = n;
n = n->m_next;
len -= m->m_len;
} else {
if (len > MHLEN)
goto bad;
MGET(m, n->m_type);
if (m == NULL)
goto bad;
m->m_len = 0;
if (n->m_flags & M_PKTHDR) {
M_MOVE_PKTHDR(m, n);
}
}
space = &m->m_dat[MLEN] - (m->m_data + m->m_len);
do {
count = min(min(max(len, max_protohdr), space), n->m_len);
memcpy((void *)(mtod(m, caddr_t) + m->m_len), mtod(n, void *), (uint)count);
len -= count;
m->m_len += count;
n->m_len -= count;
space -= count;
if (n->m_len)
n->m_data += count;
else
n = m_free(n);
} while (len > 0 && n);
if (len > 0) {
printf("m_pullup: failed: len = %d\n", len);
(void)m_free(m);
goto bad;
}
m->m_next = n;
return (m);
bad:
m_freem(n);
MPFail++;
return (NULL);
}
/*
* Copy data from a buffer back into the indicated mbuf chain,
* starting "off" bytes from the beginning, extending the mbuf
* chain if necessary. The mbuf needs to be properly initalized
* including the setting of m_len.
*/
void m_copyback(struct mbuf *m0, int off, int len, caddr_t cp)
{
int mlen;
struct mbuf *m = m0, *n;
int totlen = 0;
if (m0 == 0)
return;
while (off > (mlen = m->m_len)) {
off -= mlen;
totlen += mlen;
if (m->m_next == 0) {
n = m_getclr(m->m_type);
if (n == 0)
goto out;
n->m_len = min(MLEN, len + off);
m->m_next = n;
}
m = m->m_next;
}
while (len > 0) {
mlen = min (m->m_len - off, len);
memcpy(off + mtod(m, caddr_t), cp, (unsigned)mlen);
cp += mlen;
len -= mlen;
mlen += off;
off = 0;
totlen += mlen;
if (len == 0)
break;
if (m->m_next == 0) {
n = m_get(m->m_type);
if (n == 0)
break;
n->m_len = min(MLEN, len);
m->m_next = n;
}
m = m->m_next;
}
out:
if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen))
m->m_pkthdr.len = totlen;
}
+68
View File
@@ -0,0 +1,68 @@
/* some misc functions... */
#include <stdio.h>
#include <strings.h>
#ifdef _KERNEL_
#include <KernelExport.h>
#endif
#include "net_misc.h"
#include "sys/socket.h"
/* Basically use dump_ to see the address plus message on a line,
* print_ to simply have the address printed with nothing else...
*/
void dump_ipv4_addr(char *msg, void *ad)
{
uint8 *b = (uint8*)ad;
printf("%s %d.%d.%d.%d\n", msg, b[0], b[1], b[2], b[3]);
}
void print_ipv4_addr(void *ad)
{
uint8 *b = (uint8*)ad;
printf("%d.%d.%d.%d", b[0], b[1], b[2], b[3]);
}
void dump_ether_addr(char *msg, void *ea)
{
uint8 *b = (uint8*)ea;
printf("%s %02x:%02x:%02x:%02x:%02x:%02x\n", msg,
b[0], b[1], b[2],
b[3], b[4], b[5]);
}
void print_ether_addr(void *ea)
{
uint8 *b = (uint8*)ea;
printf("%02x:%02x:%02x:%02x:%02x:%02x",
b[0], b[1], b[2],
b[3], b[4], b[5]);
}
void dump_buffer(char *buffer, int len)
{
uint8 *b = (uint8 *)buffer;
int i;
printf (" ");
for (i=0;i<len;i++) {
if (i%16 == 0)
printf("\n ");
if (i%2 == 0)
printf(" %02x", b[i]);
else
printf("%02x ", b[i]);
}
printf("\n\n");
}
int compare_sockaddr(struct sockaddr *a, struct sockaddr *b)
{
if (a->sa_len == 4) /* IPv4 address, basically a uint32 */
return (*(a->sa_data) = *(b->sa_data));
return memcmp((void*)a->sa_data, (void*)b->sa_data, a->sa_len);
}
+229
View File
@@ -0,0 +1,229 @@
/* net_timer.h - a small and more or less inaccurate timer for net modules.
** The registered hooks will be called in the thread of the timer.
**
** Initial version by Axel Dörfler, [email protected]
**
** This file may be used under the terms of the OpenBeOS License.
*/
#include <OS.h>
#include <malloc.h>
#ifdef _KERNEL_
#include <KernelExport.h>
#endif
#include "net_timer.h"
struct timer_entry {
struct timer_entry *te_next;
net_timer_hook te_hook;
void *te_data;
bigtime_t te_interval;
bigtime_t te_until;
net_timer_id te_id;
};
struct timer_info {
struct timer_entry *ti_first;
sem_id ti_lock;
sem_id ti_wait;
int32 ti_counter;
volatile int32 ti_inUse;
};
int32 net_timer(void *_data);
struct timer_info gTimerInfo;
status_t
net_init_timer(void)
{
thread_id thread;
memset(&gTimerInfo,0,sizeof(struct timer_info));
gTimerInfo.ti_lock = create_sem(1,"net timer lock");
if (gTimerInfo.ti_lock < B_OK)
return B_ERROR;
gTimerInfo.ti_wait = create_sem(0,"net timer wait");
if (gTimerInfo.ti_wait < B_OK)
return B_ERROR;
#ifdef _KERNEL_
set_sem_owner(gTimerInfo.ti_lock, B_SYSTEM_TEAM);
set_sem_owner(gTimerInfo.ti_wait, B_SYSTEM_TEAM);
thread = spawn_kernel_thread(net_timer,"net timer",B_NORMAL_PRIORITY,&gTimerInfo);
#else
thread = spawn_thread(net_timer,"net timer",B_NORMAL_PRIORITY,&gTimerInfo);
#endif
if (thread < B_OK)
return thread;
return resume_thread(thread);
}
void
net_shutdown_timer(void)
{
struct timer_entry *te,*next;
int32 tries = 20;
delete_sem(gTimerInfo.ti_wait);
delete_sem(gTimerInfo.ti_lock);
gTimerInfo.ti_wait = -1;
gTimerInfo.ti_lock = -1;
// make sure the structure isn't used anymore
while (gTimerInfo.ti_inUse != 0 && tries-- > 0)
snooze(1000);
// free the remaining timer entries
for (te = gTimerInfo.ti_first;te;te = next) {
next = te->te_next;
free(te);
}
}
int32
net_timer(void *_data)
{
struct timer_info *timer = (struct timer_info *)_data;
status_t status = B_OK;
do {
bigtime_t timeout = B_INFINITE_TIMEOUT;
struct timer_entry *te;
// get access to the info structure
if (status == B_TIMED_OUT || status == B_OK) {
if (acquire_sem(timer->ti_lock) == B_OK) {
for (te = timer->ti_first;te;te = te->te_next) {
// new entry?
if (te->te_until == -1)
te->te_until = system_time() + te->te_interval;
// execute timer?
if (te->te_until < system_time()) {
te->te_until += te->te_interval;
te->te_hook(te->te_data);
}
// calculate new timeout
if (te->te_until < timeout)
timeout = te->te_until;
}
release_sem(timer->ti_lock);
}
}
status = acquire_sem_etc(timer->ti_wait,1,B_ABSOLUTE_TIMEOUT,timeout);
// the wait sem normally can't be acquired, so we
// have to look at the status value the call returns:
//
// B_OK - someone wanted to notify us
// B_TIMED_OUT - look for timers to be executed
// B_BAD_SEM_ID - our sem got deleted
} while (status != B_BAD_SEM_ID);
return 0;
}
net_timer_id
net_add_timer(net_timer_hook hook,void *data,bigtime_t interval)
{
struct timer_entry *te;
status_t status;
if (interval < 100)
return B_BAD_VALUE;
atomic_add(&gTimerInfo.ti_inUse,1);
// get access to the timer info structure
status = acquire_sem(gTimerInfo.ti_lock);
if (status < B_OK) {
atomic_add(&gTimerInfo.ti_inUse,-1);
return status;
}
te = (struct timer_entry *)malloc(sizeof(struct timer_entry));
if (te == NULL) {
atomic_add(&gTimerInfo.ti_inUse,-1);
release_sem(gTimerInfo.ti_lock);
return B_NO_MEMORY;
}
te->te_hook = hook;
te->te_data = data;
te->te_interval = interval;
te->te_until = -1;
te->te_id = ++gTimerInfo.ti_counter;
// add the new entry
te->te_next = gTimerInfo.ti_first;
gTimerInfo.ti_first = te;
atomic_add(&gTimerInfo.ti_inUse,-1);
release_sem(gTimerInfo.ti_lock);
// notify timer about the change
release_sem(gTimerInfo.ti_wait);
return te->te_id;
}
status_t
net_remove_timer(net_timer_id id)
{
struct timer_entry *te,*last;
status_t status;
if (id <= B_OK)
return B_BAD_VALUE;
atomic_add(&gTimerInfo.ti_inUse,1);
// get access to the timer info structure
status = acquire_sem(gTimerInfo.ti_lock);
if (status < B_OK) {
atomic_add(&gTimerInfo.ti_inUse,-1);
return status;
}
// search the list for the right timer
// little hack that relies on ti_first being on the same position
// in the structure as te_next
last = (struct timer_entry *)&gTimerInfo;
for (te = gTimerInfo.ti_first;te;te = te->te_next) {
if (te->te_id == id) {
last->te_next = te->te_next;
free(te);
break;
}
last = te;
}
atomic_add(&gTimerInfo.ti_inUse,-1);
release_sem(gTimerInfo.ti_lock);
if (te == NULL)
return B_ENTRY_NOT_FOUND;
// notify timer about the change
release_sem(gTimerInfo.ti_wait);
return B_OK;
}
+150
View File
@@ -0,0 +1,150 @@
/* nhash.c
* net hash
*/
#include <stdio.h>
#include <stdlib.h>
#include "net_malloc.h"
#include "nhash.h"
#define MAX_INITIAL 15;
net_hash *nhash_make(void)
{
net_hash *nn;
nn = (net_hash *)malloc(sizeof(net_hash));
if (!nn)
return NULL;
nn->count = 0;
nn->max = MAX_INITIAL;
nn->array = (net_hash_entry **)malloc(sizeof(net_hash_entry) * (nn->max + 1));
memset(nn->array, 0, sizeof(net_hash_entry) * (nn->max +1));
pool_init(&nn->pool, sizeof(net_hash_entry));
if (!nn->pool)
return NULL;
return nn;
}
net_hash_index *nhash_next(net_hash_index *hi)
{
hi->this = hi->next;
while (!hi->this) {
if (hi->index > hi->nh->max)
return NULL;
hi->this = hi->nh->array[hi->index++];
}
hi->next = hi->this->next;
return hi;
}
net_hash_index *nhash_first(net_hash *nh)
{
net_hash_index *hi = &nh->iterator;
hi->nh = nh;
hi->index = 0;
hi->this = hi->next = NULL;
return nhash_next(hi);
}
static void expand_array(net_hash *nh)
{
net_hash_index *hi;
net_hash_entry **new_array;
int new_max = nh->max * 2 +1;
int i;
new_array = (net_hash_entry **)malloc(sizeof(net_hash_entry) * new_max);
memset(new_array, 0, sizeof(net_hash_entry) * new_max);
for (hi = nhash_first(nh); hi; hi = nhash_next(hi)) {
i = hi->this->hash & new_max;
hi->this->next = new_array[i];
new_array[i] = hi->this;
}
free(nh->array);
nh->array = new_array;
nh->max = new_max;
}
void nhash_this(net_hash_index *hi, const void **key, ssize_t *klen,
void **val)
{
if (key) *key = hi->this->key;
if (klen) *klen = hi->this->klen;
if (val) *val = (void*)hi->this->val;
}
static net_hash_entry **find_entry(net_hash *nh, const void *key,
ssize_t klen, const void *val)
{
net_hash_entry **hep;
net_hash_entry *he;
const unsigned char *p;
int hash = 0;
ssize_t i;
if (!nh)
return NULL;
for (p=key, i=klen; i; i--, p++)
hash = hash * 33 + *p;
for (hep = &nh->array[hash & nh->max], he = *hep; he;
hep = &he->next, he = *hep) {
if (he->hash == hash && he->klen == klen
&& memcmp(he->key, key, klen) == 0) {
break;
}
}
if (he || !val)
return hep;
/* add a new linked-list entry */
he = (net_hash_entry *)pool_get(nh->pool);
he->next = NULL;
he->hash = hash;
he->key = key;
he->klen = klen;
he->val = val;
*hep = he;
nh->count++;
return hep;
}
void *nhash_get(net_hash *nh, const void *key, ssize_t klen)
{
net_hash_entry *he;
he = *find_entry(nh, key, klen, NULL);
if (he)
return (void*)he->val;
else
return NULL;
}
void nhash_set(net_hash *nh, const void *key, ssize_t klen, const void *val)
{
net_hash_entry **hep;
net_hash_entry *old;
hep = find_entry(nh, key, klen, val);
if (*hep) {
if (!val) {
/* delete it */
old = *hep;
*hep = (*hep)->next;
--nh->count;
pool_put(nh->pool, old);
} else {
/* replace it */
(*hep)->val = val;
if (nh->count > nh->max)
expand_array(nh);
}
}
}
+324
View File
@@ -0,0 +1,324 @@
/* pools.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "pools.h"
#include "net_misc.h"
#include "net_malloc.h"
#ifdef _KERNEL_
#include <KernelExport.h>
#define AREA_ADDR_FLAG B_ANY_KERNEL_ADDRESS
#define AREA_FLAGS B_NO_LOCK
#else
#define AREA_ADDR_FLAG B_ANY_ADDRESS
#define AREA_FLAGS B_FULL_LOCK
#endif
static sem_id init_sem = -1;
#define ROUND_TO_PAGE_SIZE(x) (((x) + (B_PAGE_SIZE) - 1) & ~((B_PAGE_SIZE) - 1))
#ifdef WALK_POOL_LIST
void walk_pool_list(struct pool_ctl *p)
{
struct pool_mem *pb = p->list;
printf("Pool: %p\n", p);
printf(" -> list = %p\n", pb);
while (pb) {
printf(" -> mem_block %p, %p\n", pb, pb->next);
pb = pb->next;
}
}
#endif
void pool_debug_walk(struct pool_ctl *p)
{
char *ptr;
int i = 1;
printf("%ld byte blocks allocated, but now free:\n\n", p->alloc_size);
#if POOL_USES_BENAPHORES
ACQUIRE_BENAPHORE(p->lock);
#else
ACQUIRE_READ_LOCK(p->lock);
#endif
ptr = p->freelist;
while (ptr) {
printf(" %02d: %p\n", i++, ptr);
ptr = ((struct free_blk*)ptr)->next;
}
#if POOL_USES_BENAPHORES
RELEASE_BENAPHORE(p->lock);
#else
RELEASE_READ_LOCK(p->lock);
#endif
}
void pool_debug(struct pool_ctl *p, char *name)
{
p->debug = 1;
if (strlen(name) < POOL_DEBUG_NAME_SZ)
strncpy(p->name, name, strlen(name));
else
strncpy(p->name, name, POOL_DEBUG_NAME_SZ);
}
static struct pool_mem *get_mem_block(struct pool_ctl *pool)
{
struct pool_mem *block;
block = (struct pool_mem *)malloc(sizeof(struct pool_mem));
if (block == NULL)
return NULL;
memset(block, 0, sizeof(*block));
block->aid = create_area("net_stack_pools_block",
(void**)&block->base_addr,
AREA_ADDR_FLAG, pool->block_size,
AREA_FLAGS,
B_READ_AREA|B_WRITE_AREA);
if (block->aid < B_OK) {
free(block);
return NULL;
}
block->mem_size = block->avail = pool->block_size;
block->ptr = block->base_addr;
INIT_BENAPHORE(block->lock, "pool_mem_lock");
if (CHECK_BENAPHORE(block->lock) >= B_OK) {
#if POOL_USES_BENAPHORES
ACQUIRE_BENAPHORE(pool->lock);
#else
ACQUIRE_WRITE_LOCK(pool->lock);
#endif
// insert block at the beginning of the pools
if (pool->list)
block->next = pool->list;
pool->list = block;
#ifdef WALK_POOL_LIST
walk_pool_list(pool);
#endif
#if POOL_USES_BENAPHORES
RELEASE_BENAPHORE(pool->lock);
#else
RELEASE_WRITE_LOCK(pool->lock);
#endif
return block;
}
UNINIT_BENAPHORE(block->lock);
delete_area(block->aid);
free(block);
return NULL;
}
status_t pool_init(struct pool_ctl **_newPool, size_t size)
{
struct pool_ctl *pool = NULL;
if (init_sem == -1)
create_sem(1, "pool_init_sem");
/* minimum block size is sizeof the free_blk structure */
if (size < sizeof(struct free_blk))
return B_BAD_VALUE;
// acquire_sem_etc(init_sem, 1, B_CAN_INTERRUPT, 0);
pool = (struct pool_ctl*)malloc(sizeof(struct pool_ctl));
if (pool == NULL)
return B_NO_MEMORY;
memset(pool, 0, sizeof(*pool));
#if POOL_USES_BENAPHORES
INIT_BENAPHORE(pool->lock, "pool_lock");
if (CHECK_BENAPHORE(pool->lock) < B_OK) {
free(pool);
return B_ERROR;
}
#else
INIT_RW_LOCK(pool->lock, "pool_lock");
if (CHECK_RW_LOCK(pool->lock) < B_OK) {
free(pool);
return B_ERROR;
}
#endif
// 4 puddles will always fit in one pool
pool->block_size = ROUND_TO_PAGE_SIZE(size * 8);
pool->alloc_size = size;
pool->list = NULL;
pool->freelist = NULL;
/* now add a first block */
get_mem_block(pool);
if (!pool->list) {
#if POOL_USES_BENAPHORES
UNINIT_BENAPHORE(pool->lock);
#else
UNINIT_RW_LOCK(pool->lock);
#endif
free(pool);
return B_NO_MEMORY;
}
*_newPool = pool;
// release_sem_etc(init_sem, 1, B_CAN_INTERRUPT);
return B_OK;
}
char *pool_get(struct pool_ctl *p)
{
/* ok, so now we look for a suitable block... */
struct pool_mem *mp = p->list;
char *rv = NULL;
#if POOL_USES_BENAPHORES
ACQUIRE_BENAPHORE(p->lock);
#else
ACQUIRE_WRITE_LOCK(p->lock);
#endif
if (p->freelist) {
/* woohoo, just grab a block! */
rv = p->freelist;
if (p->debug)
printf("%s: allocating %p, setting freelist to %p\n",
p->name, p->freelist,
((struct free_blk*)rv)->next);
p->freelist = ((struct free_blk*)rv)->next;
#if POOL_USES_BENAPHORES
RELEASE_BENAPHORE(p->lock);
#else
RELEASE_WRITE_LOCK(p->lock);
#endif
memset(rv, 0, p->alloc_size);
return rv;
}
#if !POOL_USES_BENAPHORES
RELEASE_WRITE_LOCK(p->lock);
ACQUIRE_READ_LOCK(p->lock);
#endif
/* no free blocks, try to allocate of the top of the memory blocks
** we must hold the global pool lock while iterating through the list!
*/
do {
ACQUIRE_BENAPHORE(mp->lock);
if (mp->avail >= p->alloc_size) {
rv = mp->ptr;
mp->ptr += p->alloc_size;
mp->avail -= p->alloc_size;
RELEASE_BENAPHORE(mp->lock);
break;
}
RELEASE_BENAPHORE(mp->lock);
} while ((mp = mp->next) != NULL);
#if POOL_USES_BENAPHORES
RELEASE_BENAPHORE(p->lock);
#else
RELEASE_READ_LOCK(p->lock);
#endif
if (rv) {
memset(rv, 0, p->alloc_size);
return rv;
}
mp = get_mem_block(p);
if (mp == NULL)
return NULL;
ACQUIRE_BENAPHORE(mp->lock);
if (mp->avail >= p->alloc_size) {
rv = mp->ptr;
mp->ptr += p->alloc_size;
mp->avail -= p->alloc_size;
}
RELEASE_BENAPHORE(mp->lock);
memset(rv, 0, p->alloc_size);
return rv;
}
void pool_put(struct pool_ctl *p, void *ptr)
{
#if POOL_USES_BENAPHORES
ACQUIRE_BENAPHORE(p->lock);
#else
ACQUIRE_WRITE_LOCK(p->lock);
#endif
memset(ptr, 0, p->alloc_size);
((struct free_blk*)ptr)->next = p->freelist;
if (p->debug) {
printf("%s: adding %p, setting next = %p\n",
p->name, ptr, p->freelist);
}
p->freelist = ptr;
if (p->debug)
printf("%s: freelist = %p\n", p->name, p->freelist);
#if POOL_USES_BENAPHORES
RELEASE_BENAPHORE(p->lock);
#else
RELEASE_WRITE_LOCK(p->lock);
#endif
}
void pool_destroy(struct pool_ctl *p)
{
struct pool_mem *mp,*temp;
if (p == NULL)
return;
/* the semaphore will be deleted, so we don't have to unlock */
ACQUIRE_WRITE_LOCK(p->lock);
mp = p->list;
while (mp != NULL) {
delete_area(mp->aid);
temp = mp;
mp = mp->next;
UNINIT_BENAPHORE(mp->lock);
free(temp);
}
#if POOL_USES_BENAPHORES
UNINIT_BENAPHORE(p->lock);
#else
UNINIT_RW_LOCK(p->lock);
#endif
free(p);
}
File diff suppressed because it is too large Load Diff
+427
View File
@@ -0,0 +1,427 @@
/* route.c */
#ifndef _KERNEL_
#include <stdio.h>
#endif
#include <kernel/OS.h>
#ifdef _KERNEL_
#include <KernelExport.h>
#endif
#include "net_malloc.h"
#include "sys/domain.h"
#include "net/route.h" /* includes net/radix.h */
#include "protocols.h"
#include "net/if.h"
int rttrash = 0; /* routes in table that should have been freed but hevn't been */
#define SA(p) ((struct sockaddr *)(p))
#define ROUNDUP(a) (a >0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
struct radix_node_head **get_rt_tables(void)
{
return (struct radix_node_head**)rt_tables;
}
struct rtentry *rtalloc1(struct sockaddr *dst, int report)
{
struct radix_node_head *rnh = rt_tables[dst->sa_family];
struct rtentry *rt;
struct radix_node *rn;
struct rtentry *newrt = NULL;
struct rt_addrinfo info;
int msgtype = RTM_MISS;
int err;
if (rnh && (rn = rnh->rnh_matchaddr((caddr_t)dst, rnh))
&& ((rn->rn_flags & RNF_ROOT) == 0)) {
newrt = rt = (struct rtentry*) rn;
if (report && (rt->rt_flags & RTF_CLONING)) {
err = rtrequest(RTM_RESOLVE, dst, NULL,
NULL, 0, &newrt);
if (err) {
newrt = rt;
rt->rt_refcnt++;
goto miss;
}
if ((rt = newrt) && (rt->rt_flags & RTF_XRESOLVE)) {
msgtype = RTM_RESOLVE;
goto miss;
}
} else
rt->rt_refcnt++;
}
/* XXX - stats? */
miss:
if (report) {
memset((caddr_t)&info, 0, sizeof(info));
info.rti_info[RTAX_DST] = dst;
//rt_missmsg(msgtype, &info, 0, err);
}
return (newrt);
}
void rtalloc(struct route *ro)
{
/* can we use what we have?? */
if (ro && ro->ro_rt && ro->ro_rt->rt_ifp &&
(ro->ro_rt->rt_flags & RTF_UP)) {
/* yes */
return;
}
/* no, get a new route */
ro->ro_rt = rtalloc1(&ro->ro_dst, 1);
}
void rtfree(struct rtentry *rt)
{
struct ifaddr *ifa;
if (!rt) {
printf("rtfree on a NULL pointer!\n");
return;
}
rt->rt_refcnt--;
if (rt->rt_refcnt <= 0 && (rt->rt_flags && RTF_UP) == 0) {
if (rt->rt_nodes->rn_flags & (RNF_ACTIVE | RNF_ROOT)) {
printf("Trying to free nodes we shouldn't be!\n");
return;
}
rttrash--;
if (rt->rt_refcnt < 0) {
printf("rtfree: %p not freed as the refcnt is negative!\n", rt);
return;
}
ifa = rt->rt_ifa;
IFAFREE(ifa);
Free(rt_key(rt));
Free(rt);
}
}
int rtrequest(int req, struct sockaddr *dst,
struct sockaddr *gateway,
struct sockaddr *netmask,
int flags,
struct rtentry **ret_nrt)
{
int error = 0;
struct rtentry *rt;
struct radix_node *rn = NULL;
struct radix_node_head *rnh;
struct ifaddr *ifa;
struct sockaddr *ndst;
#define snderr(x) {error = x; goto bad; }
if ((rnh = rt_tables[dst->sa_family]) == NULL)
snderr(ESRCH);
if (flags & RTF_HOST)
netmask = NULL;
switch(req) {
case RTM_DELETE:
if ((rn = rnh->rnh_deladdr(dst, netmask, rnh)) == NULL)
snderr(ESRCH);
if (rn->rn_flags & (RNF_ACTIVE | RNF_ROOT)) {
/* XXX - should be panic */
printf("rtrequest: delete: cannot delete route!\n");
return -1;
}
rt = (struct rtentry *)rn;
rt->rt_flags &= ~RTF_UP; /* mark route as down */
if (rt->rt_gwroute) {
rt = rt->rt_gwroute;
RTFREE(rt);
(rt = (struct rtentry*)rn)->rt_gwroute = NULL;
}
if ((ifa = rt->rt_ifa) && ifa->ifa_rtrequest)
ifa->ifa_rtrequest(RTM_DELETE, rt, NULL);
rttrash++;
if (ret_nrt)
*ret_nrt = rt;
else if (rt->rt_refcnt <= 0) {
rt->rt_refcnt++;
rtfree(rt);
}
break;
case RTM_RESOLVE:
if (ret_nrt == NULL || (rt = *ret_nrt) == NULL)
snderr(EINVAL);
ifa = rt->rt_ifa;
flags = rt->rt_flags & ~RTF_CLONING;
gateway = rt->rt_gateway;
if ((netmask = rt->rt_genmask) == NULL)
flags |= RTF_HOST;
/* fall through */
goto makeroute;
case RTM_ADD:
/* can we find a route to it? */
if ((ifa = ifa_ifwithroute(flags, dst, gateway)) == NULL) {
printf("ENETUNREACH!\n");
snderr(ENETUNREACH);
}
makeroute:
R_Malloc(rt, struct rtentry *, sizeof(*rt));
if (!rt)
snderr(ENOMEM);
Bzero(rt, sizeof(*rt));
rt->rt_flags = RTF_UP | flags;
if (rt_setgate(rt, dst, gateway)) {
Free(rt);
snderr(ENOMEM);
}
ndst = rt_key(rt);
if (netmask)
rt_maskedcopy(dst, ndst, netmask);
else
Bcopy(dst, ndst, dst->sa_len);
rn = rnh->rnh_addaddr((caddr_t) ndst, (caddr_t) netmask,
rnh, rt->rt_nodes);
if (!rn) {
if (rt->rt_gwroute)
rtfree(rt->rt_gwroute);
Free(rt_key(rt));
Free(rt);
snderr(EEXIST);
}
ifa->ifa_refcnt++;
rt->rt_ifa = ifa;
rt->rt_ifp = ifa->ifa_ifp;
/* if we've fallen through - copy metrics */
if (req == RTM_RESOLVE)
rt->rt_rmx = (*ret_nrt)->rt_rmx;
if (ifa->ifa_rtrequest)
ifa->ifa_rtrequest(req, rt, SA(ret_nrt ? *ret_nrt : 0));
if (ret_nrt) {
*ret_nrt = rt;
rt->rt_refcnt++;
}
break;
}
bad:
return (error);
}
struct ifaddr *ifa_ifwithroute(int flags,
struct sockaddr *dst,
struct sockaddr *gateway)
{
struct ifaddr *ifa;
if ((flags & RTF_GATEWAY) == 0) {
/*
* If we are adding a route to an interface,
* and the interface is a pt to pt link
* we should search for the destination
* as our clue to the interface. Otherwise
* we can use the local address.
*/
ifa = NULL;
if (flags & RTF_HOST)
ifa = ifa_ifwithdstaddr(dst);
if (ifa == NULL)
ifa = ifa_ifwithaddr(gateway);
} else {
/*
* If we are adding a route to a remote net
* or host, the gateway may still be on the
* other end of a pt to pt link.
*/
ifa = ifa_ifwithdstaddr(gateway);
}
if (ifa == NULL)
ifa = ifa_ifwithnet(gateway);
if (ifa == NULL) {
struct rtentry *rt = rtalloc1(gateway, 0);
if (rt == NULL)
return (NULL);
rt->rt_refcnt--;
/* The gateway must be local if the same address family. */
if ((rt->rt_flags & RTF_GATEWAY) &&
rt_key(rt)->sa_family == dst->sa_family)
return (0);
if ((ifa = rt->rt_ifa) == NULL)
return (NULL);
}
if (ifa->ifa_addr->sa_family != dst->sa_family) {
struct ifaddr *oifa = ifa;
ifa = ifaof_ifpforaddr(dst, ifa->ifa_ifp);
if (ifa == NULL)
ifa = oifa;
}
return (ifa);
}
int rt_setgate(struct rtentry *rt0,
struct sockaddr *dst,
struct sockaddr *gate)
{
caddr_t new, old = NULL;
int dlen = ROUNDUP(dst->sa_len), glen = ROUNDUP(gate->sa_len);
struct rtentry *rt = rt0;
if (rt->rt_gateway == NULL || glen > ROUNDUP(rt->rt_gateway->sa_len)) {
old = (caddr_t)rt_key(rt);
R_Malloc(new, caddr_t, dlen + glen);
if (new == NULL)
return 1;
rt->rt_nodes->rn_key = new;
} else {
new = rt->rt_nodes->rn_key;
old = NULL;
}
Bcopy(gate, (rt->rt_gateway = (struct sockaddr *)(new + dlen)), glen);
if (old) {
Bcopy(dst, new, dlen);
Free(old);
}
if (rt->rt_gwroute != NULL) {
rt = rt->rt_gwroute;
RTFREE(rt);
rt = rt0;
rt->rt_gwroute = NULL;
}
if (rt->rt_flags & RTF_GATEWAY) {
rt->rt_gwroute = rtalloc1(gate, 1);
/*
* If we switched gateways, grab the MTU from the new
* gateway route if the current MTU is 0 or greater
* than the MTU of gateway.
*/
if (rt->rt_gwroute && !(rt->rt_rmx.rmx_locks & RTV_MTU) &&
(rt->rt_rmx.rmx_mtu == 0 ||
rt->rt_rmx.rmx_mtu > rt->rt_gwroute->rt_rmx.rmx_mtu)) {
rt->rt_rmx.rmx_mtu = rt->rt_gwroute->rt_rmx.rmx_mtu;
}
}
return 0;
}
void rt_maskedcopy(struct sockaddr *src,
struct sockaddr *dst,
struct sockaddr *netmask)
{
uchar *cp1 = (uchar *)src;
uchar *cp2 = (uchar *)dst;
uchar *cp3 = (uchar *)netmask;
uchar *cplim = cp2 + *cp3;
uchar *cplim2 = cp2 + *cp1;
*cp2++ = *cp1++; *cp2++ = *cp1++; /* copies sa_len & sa_family */
cp3 += 2;
if (cplim > cplim2)
cplim = cplim2;
while (cp2 < cplim)
*cp2++ = *cp1++ & *cp3++;
if (cp2 < cplim2)
memset((caddr_t)cp2, 0, (unsigned)(cplim2 - cp2));
}
void ifafree(struct ifaddr *ifa)
{
if (ifa == NULL) {
printf("ifafree");
return;
}
if (ifa->ifa_refcnt == 0)
free(ifa);
else
ifa->ifa_refcnt--;
}
/*
* Set up a routing table entry, normally
* for an interface.
*/
int rtinit(struct ifaddr *ifa, int cmd, int flags)
{
struct rtentry *rt;
struct sockaddr *dst;
struct sockaddr *deldst;
struct mbuf *m = NULL;
struct rtentry *nrt = NULL;
int error;
dst = flags & RTF_HOST ? ifa->ifa_dstaddr : ifa->ifa_addr;
if (cmd == RTM_DELETE) {
if ((flags & RTF_HOST) == 0 && ifa->ifa_netmask) {
m = m_get(MT_SONAME);
if (m == NULL)
return(ENOBUFS);
deldst = mtod(m, struct sockaddr *);
rt_maskedcopy(dst, deldst, ifa->ifa_netmask);
dst = deldst;
}
if ((rt = rtalloc1(dst, 0)) != NULL) {
rt->rt_refcnt--;
if (rt->rt_ifa != ifa) {
if (m != NULL)
(void) m_free(m);
return (flags & RTF_HOST ? EHOSTUNREACH : ENETUNREACH);
}
}
}
error = rtrequest(cmd, dst, ifa->ifa_addr, ifa->ifa_netmask,
flags | ifa->ifa_flags, &nrt);
if (cmd == RTM_DELETE && error == 0 && (rt = nrt) != NULL) {
/* XXX - add this when we have routing sockets!
rt_newaddrmsg(cmd, ifa, error, nrt);
*/
if (rt->rt_refcnt <= 0) {
rt->rt_refcnt++;
rtfree(rt);
}
}
if (cmd == RTM_ADD && error == 0 && (rt = nrt) != NULL) {
rt->rt_refcnt--;
if (rt->rt_ifa != ifa) {
printf("rtinit: wrong ifa (%p) was (%p)\n", ifa, rt->rt_ifa);
if (rt->rt_ifa->ifa_rtrequest)
rt->rt_ifa->ifa_rtrequest(RTM_DELETE, rt, NULL);
IFAFREE(rt->rt_ifa);
rt->rt_ifa = ifa;
rt->rt_ifp = ifa->ifa_ifp;
rt->rt_rmx.rmx_mtu = ifa->ifa_ifp->if_mtu;
ifa->ifa_refcnt++;
if (ifa->ifa_rtrequest)
ifa->ifa_rtrequest(RTM_ADD, rt, NULL);
}
/* XXX - add this when we have routing sockets!
rt_newaddrmsg(cmd, ifa, error, nrt);
*/
}
return (error);
}
void rtable_init(void **table)
{
struct domain *dom;
for (dom = domains; dom; dom = dom->dom_next)
if (dom->dom_rtattach)
dom->dom_rtattach(&table[dom->dom_family], dom->dom_rtoffset);
}
void route_init(void)
{
rn_init();
rtable_init((void**)rt_tables);
}
+307
View File
@@ -0,0 +1,307 @@
/* socket "server" */
#include <stdio.h>
#include <kernel/OS.h>
#ifdef _KERNEL_
#include <KernelExport.h>
#endif
#include "sys/socket.h"
#include "sys/socketvar.h"
#include "pools.h"
#include "netinet/in_pcb.h"
#include "net_misc.h"
#include "protocols.h"
uint32 sb_max = SB_MAX; /* hard value, recompile needed to alter :( */
/*
* Allot mbufs to a sockbuf.
* Attempt to scale mbmax so that mbcnt doesn't become limiting
* if buffering efficiency is near the normal case.
*/
int sbreserve(struct sockbuf *sb, uint32 cc)
{
uint64 dd = (uint64)cc;
uint64 ee = (sb_max * MCLBYTES) / ((MSIZE) + (MCLBYTES));
if (cc == 0)
return 0;
if (dd > ee)
return 0;
sb->sb_hiwat = cc;
sb->sb_mbmax = min((cc * 2), sb_max);
if (sb->sb_lowat > sb->sb_hiwat)
sb->sb_lowat = sb->sb_hiwat;
return (1);
}
void sbdrop(struct sockbuf *sb, int len)
{
struct mbuf *m, *mn;
struct mbuf *next;
next = (m = sb->sb_mb) ? m->m_nextpkt : NULL;
while (len > 0) {
if (m == NULL) {
if (next == NULL)
return;
m = next;
next = m->m_nextpkt;
continue;
}
if (m->m_len > len) {
m->m_len -= len;
m->m_data += len;
sb->sb_cc -= len;
break;
}
len -= m->m_len;
sbfree(sb, m);
MFREE(m, mn);
m = mn;
}
while (m && m->m_len == 0) {
sbfree(sb, m);
MFREE(m, mn);
m = mn;
}
if (m) {
sb->sb_mb = m;
m->m_nextpkt = next;
} else
sb->sb_mb = next;
}
/*
* Free all mbufs in a sockbuf.
* Check that all resources are reclaimed.
*/
void sbflush(struct sockbuf *sb)
{
if (sb->sb_flags & SB_LOCK) {
return;
}
while (sb->sb_mbcnt)
sbdrop(sb, (int)sb->sb_cc);
if (sb->sb_cc || sb->sb_mb)
return;
}
/*
* Free mbufs held by a socket, and reserved mbuf space.
*/
void sbrelease(struct sockbuf *sb)
{
sbflush(sb);
sb->sb_hiwat = sb->sb_mbmax = 0;
}
void sbappend(struct sockbuf *sb, struct mbuf *m)
{
struct mbuf *n;
if (!m)
return;
if ((n = sb->sb_mb) != NULL) {
while (n->m_nextpkt)
n = n->m_nextpkt;
do {
if (n->m_flags & M_EOR) {
sbappendrecord(sb, m); /* XXXXXX!!!! */
return;
}
} while (n->m_next && (n = n->m_next));
}
sbcompress(sb, m, n);
}
void sbappendrecord(struct sockbuf *sb, struct mbuf *m0)
{
struct mbuf *m;
if (!m0)
return;
if ((m = sb->sb_mb) != NULL)
while (m->m_nextpkt)
m = m->m_nextpkt;
/*
* Put the first mbuf on the queue.
* Note this permits zero length records.
*/
sballoc(sb, m0);
if (m)
m->m_nextpkt = m0;
else
sb->sb_mb = m0;
m = m0->m_next;
m0->m_next = 0;
if (m && (m0->m_flags & M_EOR)) {
m0->m_flags &= ~M_EOR;
m->m_flags |= M_EOR;
}
sbcompress(sb, m, m0);
}
int sbappendaddr(struct sockbuf *sb, struct sockaddr *asa,
struct mbuf *m0, struct mbuf *control)
{
struct mbuf *m, *n;
int space = asa->sa_len;
if (m0 && (m0->m_flags & M_PKTHDR) == 0)
return(-1);
if (m0)
space += m0->m_pkthdr.len;
for (n = control; n; n = n->m_next) {
space += n->m_len;
if (n->m_next == 0) /* keep pointer to last control buf */
break;
}
if (space > sbspace(sb))
return (0);
if (asa->sa_len > MLEN)
return (0);
MGET(m, MT_SONAME);
if (m == NULL)
return (0);
m->m_len = asa->sa_len;
memcpy(mtod(m, caddr_t), (caddr_t)asa, asa->sa_len);
if (n)
n->m_next = m0; /* concatenate data to control */
else
control = m0;
m->m_next = control;
for (n = m; n; n = n->m_next)
sballoc(sb, n);
if ((n = sb->sb_mb) != NULL) {
while (n->m_nextpkt)
n = n->m_nextpkt;
n->m_nextpkt = m;
} else
sb->sb_mb = m;
return (1);
}
void sbcompress(struct sockbuf *sb, struct mbuf *m, struct mbuf *n)
{
int eor = 0;
struct mbuf *o;
while (m) {
eor |= m->m_flags & M_EOR;
if (m->m_len == 0 &&
(eor == 0 ||
(((o = m->m_next) || (o = n)) &&
o->m_type == m->m_type))) {
m = m_free(m);
continue;
}
if (n && (n->m_flags & (M_EXT | M_EOR)) == 0 &&
(n->m_data + n->m_len + m->m_len) < &n->m_dat[MLEN] &&
n->m_type == m->m_type) {
memcpy(mtod(n, caddr_t) + n->m_len, mtod(m, caddr_t),
(unsigned)m->m_len);
n->m_len += m->m_len;
sb->sb_cc += m->m_len;
m = m_free(m);
continue;
}
if (n)
n->m_next = m;
else
sb->sb_mb = m;
sballoc(sb, m);
n = m;
m->m_flags &= ~M_EOR;
m = m->m_next;
n->m_next = 0;
}
if (eor) {
if (n)
n->m_flags |= eor;
else
printf("semi-panic: sbcompress\n");
}
}
void sbdroprecord(struct sockbuf *sb)
{
struct mbuf *m, *mn;
m = sb->sb_mb;
if (m) {
sb->sb_mb = m->m_nextpkt;
do {
sbfree(sb, m);
MFREE(m, mn);
} while ((m = mn) != NULL);
}
}
int sbwait(struct sockbuf *sb)
{
if (sb->sb_cc > 0)
return 0;
sb->sb_flags |= SB_WAIT;
return nsleep(sb->sb_pop, "sbwait", sb->sb_timeo);
}
void sbinsertoob(struct sockbuf *sb, struct mbuf *m0)
{
struct mbuf *m;
struct mbuf **mp;
if (m0 == NULL)
return;
for (mp = &sb->sb_mb; (m = *mp) != NULL; mp = &((*mp)->m_nextpkt)) {
again:
switch (m->m_type) {
case MT_OOBDATA:
continue; /* WANT next train */
case MT_CONTROL:
if ((m = m->m_next) != NULL)
goto again; /* inspect THIS
* train further */
}
break;
}
/*
* Put the first mbuf on the queue.
* Note this permits zero length records.
*/
sballoc(sb, m0);
m0->m_nextpkt = *mp;
*mp = m0;
m = m0->m_next;
m0->m_next = 0;
if (m && (m0->m_flags & M_EOR)) {
m0->m_flags &= ~M_EOR;
m->m_flags |= M_EOR;
}
sbcompress(sb, m, m0);
}
/*
* Lock a sockbuf already known to be locked;
* return any error returned from sleep (EINTR).
*/
int sb_lock(struct sockbuf *sb)
{
int error;
while (sb->sb_flags & SB_LOCK) {
sb->sb_flags |= SB_WANT;
error = nsleep(sb->sb_sleep, "sb_lock", 0);
if (error)
return (error);
}
sb->sb_flags |= SB_LOCK;
return (0);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,5 @@
SubDir OBOS_TOP src add-ons kernel network interfaces ;
SubInclude OBOS_TOP src add-ons kernel network interfaces ethernet ;
SubInclude OBOS_TOP src add-ons kernel network interfaces loopback ;
@@ -0,0 +1,2 @@
SubDir OBOS_TOP src add-ons kernel network interfaces ethernet ;
@@ -0,0 +1,938 @@
/* ethernet.c
* ethernet encapsulation
*/
#include <stdio.h>
#include <stdlib.h>
#include <kernel/OS.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <malloc.h>
#include <time.h>
#include "protocols.h"
#include "netinet/in_var.h"
#include "sys/protosw.h"
#include "net/if.h"
#include "net/if_arp.h"
#include "net/if_dl.h"
#include "netinet/if_ether.h"
#include "sys/socket.h"
#include "sys/sockio.h"
#include "net/route.h"
#include "core_module.h"
#include "core_funcs.h"
#include "net_timer.h"
#define ETHERNET_MODULE_PATH "network/interfaces/ethernet"
#ifdef _KERNEL_MODE
#include <KernelExport.h>
#define spawn_thread spawn_kernel_thread
/* forward prototypes */
int ether_dev_start(ifnet *dev);
int ether_dev_stop (ifnet *dev);
#endif
static int32 std_ops(int32 op, ...);
/* Local variables */
static struct protosw *proto[IPPROTO_MAX];
static struct core_module_info *core = NULL;
static net_timer_id arptimer_id;
static struct ether_device *ether_devices = NULL; /* list of ethernet devices */
static struct ifq *etherq = NULL;
static thread_id ether_rxt = -1;
static int arpt_prune = (5 * 60); /* time interval we prune the arp cache? 5 minutes */
static int arpt_keep = (20 * 60); /* length of time we keep entries... (20 mins) */
static int arpt_down = 20; /* seconds between arp flooding */
static int arp_maxtries = 5; /* max tries before a pause */
static int32 arp_inuse = 0; /* how many entries do we have? */
static int32 arp_allocated = 0; /* how many arp entries have we created? */
/* Prototypes */
int32 ether_input(void *data);
int ether_output(struct ifnet *ifp, struct mbuf *buf, struct sockaddr *dst,
struct rtentry *rt0);
static int ether_ioctl(struct ifnet *ifp, int cmd, caddr_t data);
int ether_dev_attach(ifnet *dev);
int ether_dev_stop(ifnet *dev);
void arp_rtrequest(int req, struct rtentry *rt, struct sockaddr *sa);
static void arpinput(struct mbuf *m);
#define DRIVER_DIRECTORY "/dev/net"
#define SIN(s) ((struct sockaddr_in*)s)
#define SDL(s) ((struct sockaddr_dl*)s)
#define rt_expire rt_rmx.rmx_expire
static uint8 ether_bcast[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
struct llinfo_arp llinfo_arp;
struct in_ifaddr *primary_addr;
static char digits[] = "0123456789abcdef";
static char *ether_sprintf(uint8 *ap)
{
register int i;
static char etherbuf[18];
register char *cp = etherbuf;
for (i = 0; i < 6; i++) {
*cp++ = digits[*ap >> 4];
*cp++ = digits[*ap++ & 0xf];
*cp++ = ':';
}
*--cp = 0;
return (etherbuf);
}
#if ARP_DEBUG
static void dump_arp(void *buffer)
{
struct ether_arp *arp = (struct ether_arp *)buffer;
printf("arp request :\n");
printf(" : hardware type : %s\n",
ntohs(arp->arp_hrd) == ARPHRD_ETHER ? "ethernet" : "unknown");
printf(" : protocol type : %s\n",
ntohs(arp->arp_pro) == ETHERTYPE_IP ? "IPv4" : "unknown");
printf(" : hardware size : %d\n", arp->arp_hln);
printf(" : protocol size : %d\n", arp->arp_pln);
printf(" : op code : ");
switch(ntohs(arp->arp_op)) {
case ARPOP_REPLY:
printf("ARP Reply\n");
break;
case ARPOP_REQUEST:
printf("ARP Request\n");
break;
default:
printf("Who knows? %04x\n", ntohs(arp->arp_op));
}
printf(" : sender : %s", ether_sprintf(&arp->arp_sha));
printf(" [%08lx]\n", ntohl(*(uint32*)&arp->arp_spa));
printf(" : target : %s", ether_sprintf(&arp->arp_tha));
printf(" [%08lx]\n", ntohl(*(uint32*)&arp->arp_tpa));
}
#endif /* ARP_DEBUG */
/* We now actually attach the device to the system... */
static void attach_device(int devid, char *driver, char *devno)
{
struct ether_device *ed;
struct ifnet *ifp;
struct ifaddr *ifa;
struct sockaddr_dl *sdl;
status_t status;
int fsz = 0;
ed = malloc(sizeof(struct ether_device));
if (!ed)
return;
memset(ed, 0, sizeof(*ed));
ifp = &ed->sc_if;
/* get the MAC address... */
status = ioctl(devid, IF_GETADDR, &ed->sc_addr, 6);
if (status < B_OK) {
printf("%s/%s: ignored: Failed to get a MAC address\n", driver, devno);
close(devid);
free(ed);
return;
}
/* Try to detrmine the MTU to use */
status = ioctl(devid, IF_GETFRAMESIZE, &fsz, sizeof(fsz));
if (status < 0) {
printf("%s/%s: IF_GETFRAMESIZE not supported, defaulting to %d\n",
driver, devno, ETHERMTU);
ifp->if_mtu = ETHERMTU;
} else
ifp->if_mtu = fsz;
ifp->devid = -1;
ifp->if_type = IFT_ETHER;
ifp->name = strdup(driver);
ifp->if_unit = atoi(devno);
ifp->if_hdrlen = 14;
ifp->if_addrlen = 6;
ifp->if_flags |= (IFF_BROADCAST|IFF_SIMPLEX|IFF_MULTICAST);
ifp->rx_thread = -1;
ifp->tx_thread = -1;
ifp->devq = etherq;
ifp->input = NULL;//&ether_input;
ifp->output = &ether_output;
ifp->stop = &ether_dev_stop;
ifp->ioctl = &ether_ioctl;
if_attach(ifp);
/* Add the MAC address to our list of addresses... */
for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) {
if ((sdl = (struct sockaddr_dl*)ifa->ifa_addr) &&
sdl->sdl_family == AF_LINK) {
sdl->sdl_type = IFT_ETHER;
sdl->sdl_alen = ifp->if_addrlen;
memcpy(LLADDR(sdl), &ed->sc_addr, ifp->if_addrlen);
break;
}
}
ed->next = NULL; /* we get added at the end of the list */
/* we maintain our own list of devices as well as the global list */
if (!ether_devices) {
ether_devices = ed;
} else {
struct ether_device *dptr = ether_devices;
while (dptr->next)
dptr = dptr->next;
dptr->next = ed;
}
}
static void open_device(char *driver, char *devno)
{
char path[PATH_MAX];
int dev;
status_t status = -1;
sprintf(path, "%s/%s/%s", DRIVER_DIRECTORY, driver, devno);
dev = open(path, O_RDWR);
if (dev < B_OK) {
printf("Unable to open %s, %ld [%s]\n", path,
status, strerror(status));
return;
}
status = ioctl(dev, IF_INIT, NULL, 0);
if (status == B_OK)
attach_device(dev, driver, devno);
close(dev);
};
static void find_devices(void)
{
DIR *dir, *driv_dir;
struct dirent *de, *dre;
char path[PATH_MAX];
dir = opendir(DRIVER_DIRECTORY);
if (!dir) {
printf("Couldn't open the directory %s\n", DRIVER_DIRECTORY);
return;
}
while ((de = readdir(dir)) != NULL) {
/* hmm, is it a driver? */
if (strcmp(de->d_name, ".") == 0 ||
strcmp(de->d_name, "..") == 0 ||
strcmp(de->d_name, "socket") == 0 ||
strcmp(de->d_name, "stack") == 0)
continue;
/* OK we assume it's a driver...but skip the ether driver
* as I don't really know what it is!
*/
if (strcmp(de->d_name, "ether") == 0)
continue;
sprintf(path, "%s/%s", DRIVER_DIRECTORY, de->d_name);
driv_dir = opendir(path);
if (!driv_dir) {
printf("I couldn't find any drivers in the %s driver directory\n",
de->d_name);
} else {
while ((dre = readdir(driv_dir)) != NULL) {
/* skip . and .. */
if (strcmp(dre->d_name, ".") == 0 ||
strcmp(dre->d_name, "..") == 0)
continue;
open_device(de->d_name, dre->d_name);
}
closedir(driv_dir);
}
}
closedir(dir);
return;
}
#if SHOW_DEBUG
static void dump_ether_details(struct mbuf *buf)
{
struct ether_header *eth = mtod(buf, struct ether_header *);
printf("Ethernet packet from ");
print_ether_addr(&eth->src);
printf(" to ");
print_ether_addr(&eth->dest);
if (buf->m_flags & M_BCAST)
printf(" BCAST");
printf(" proto ");
switch (eth->type) {
case ETHER_ARP:
printf("ARP\n");
break;
case ETHER_RARP:
printf("RARP\n");
break;
case ETHER_IPV4:
printf("IPv4\n");
break;
case ETHER_IPV6:
printf("IPv6\n");
break;
default:
printf("unknown (%04x)\n", eth->type);
}
}
#endif
int32 ether_input(void *data)
{
struct mbuf *m;
struct ether_header *eth;
int len;
while (1) {
len = sizeof(struct ether_header);
acquire_sem_etc(etherq->pop, 1, B_CAN_INTERRUPT, 0);
IFQ_DEQUEUE(etherq, m);
if (!m)
continue;
eth = mtod(m, struct ether_header *);
eth->ether_type = ntohs(eth->ether_type);
if (memcmp((void*)&eth->ether_dhost, (void*)&ether_bcast, 6) == 0)
m->m_flags |= M_BCAST;
if (eth->ether_dhost[0] & 1)
m->m_flags |= M_MCAST;
#if SHOW_DEBUG
dump_ether_details(buf);
#endif
m_adj(m, len);
switch(eth->ether_type) {
case ETHERTYPE_ARP:
arpinput(m);
break;
case ETHERTYPE_IP:
if (proto[IPPROTO_IP] && proto[IPPROTO_IP]->pr_input)
proto[IPPROTO_IP]->pr_input(m, 0);
else
printf("proto[%d] = %p, not called...\n", IPPROTO_IP,
proto[IPPROTO_IP]);
break;
case ETHERTYPE_PPPOEDISC:
case ETHERTYPE_PPPOE:
printf("PPPoE packet detected...not yet implemented :)\n");
m_freem(m);
break;
default:
printf("Couldn't process unknown protocol %04x\n", eth->ether_type);
m_freem(m);
}
}
return 0;
}
#define senderr(e) { error = (e); goto bad; }
int ether_output(struct ifnet *ifp, struct mbuf *buf, struct sockaddr *dst,
struct rtentry *rt0)
{
struct ether_header *eh;
struct rtentry *rt;
struct arpcom *ac = (struct arpcom*)ifp;
uint8 edst[6];
int off;
uint16 type;
struct mbuf *mcopy = NULL;
int error = 0;
if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) != (IFF_UP | IFF_RUNNING))
senderr(ENETDOWN);
if ((rt = rt0)) {
if ((rt->rt_flags & RTF_UP) == 0) {
if ((rt0 = rt = rtalloc1(dst, 1)) != NULL)
rt->rt_refcnt--;
else
senderr(EHOSTUNREACH);
}
if (rt->rt_flags & RTF_GATEWAY) {
if (!rt->rt_gwroute)
goto lookup;
if (((rt = rt->rt_gwroute)->rt_flags & RTF_UP) == 0) {
rtfree(rt0);
rt = rt0;
lookup:
rt->rt_gwroute = rtalloc1(rt->rt_gateway, 1);
if ((rt = rt->rt_gwroute) == NULL)
senderr(EHOSTUNREACH);
}
}
if (rt->rt_flags & RTF_REJECT)
if (rt->rt_expire == 0 ||
rt->rt_expire > real_time_clock()) {
/* XXX - add test for expired here... */
printf("flags & RTF_REJECT\n");
printf("Error: %s\n", rt == rt0 ? "EHOSTDOWN" : "EHOSTUNREACH");
senderr(rt == rt0 ? EHOSTDOWN : EHOSTUNREACH);
}
}
switch (dst->sa_family) {
case AF_INET:
if (!arpresolve(ac, rt, buf, dst, edst)) {
return 0;
}
if ((buf->m_flags & M_BCAST) && (ifp->if_flags & IFF_SIMPLEX))
mcopy = m_copym(buf, 0, (int)M_COPYALL);
off = buf->m_pkthdr.len - buf->m_len;
type = ETHERTYPE_IP;
break;
case AF_UNSPEC:
eh = (struct ether_header*)dst->sa_data;
memcpy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof(edst));
type = eh->ether_type;
break;
}
/* Hmmm, can't find a good way of getting this to work without expanding the
* macro for the kernel case, so it's here expanded. I've found that using the
* directly causes segafults. Bear in mind we want ALL allocation/free actions
* to take place in the core to keep as small a memory footprint as possible.
*/
#define M_LEADINGSPACE(m) \
((m)->m_flags & M_EXT ? (m)->m_data - (m)->m_ext.ext_buf : \
(m)->m_flags & M_PKTHDR ? (m)->m_data - (m)->m_pktdat : \
(m)->m_data - (m)->m_dat)
if (M_LEADINGSPACE(buf) >= sizeof(struct ether_header)) {
buf->m_data -= sizeof(struct ether_header);
buf->m_len += sizeof(struct ether_header);
} else
buf = m_prepend(buf, sizeof(struct ether_header));
if (buf && buf->m_flags & M_PKTHDR)
buf->m_pkthdr.len += sizeof(struct ether_header);
if (buf == NULL)
senderr(ENOMEM);
eh = mtod(buf, struct ether_header*);
type = htons(type);
memcpy(&eh->ether_type, &type, sizeof(eh->ether_type));
memcpy(&eh->ether_dhost, edst, sizeof(edst));
memcpy(&eh->ether_shost, ac->ac_enaddr, sizeof(eh->ether_shost));
IFQ_ENQUEUE(ifp->txq, buf);
return error;
bad:
if (buf)
m_free(buf);
printf("ether_output: returning %d\n", error);
return error;
}
static struct llinfo_arp *arplookup(uint32 addr, int create, int proxy)
{
struct rtentry *rt;
static struct sockaddr_inarp sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_len = sizeof(sin);
sin.sin_addr.s_addr = addr;
sin.sin_other = proxy ? SIN_PROXY : 0;
rt = rtalloc1((struct sockaddr*) &sin, create);
if (!rt)
return NULL;
rt->rt_refcnt--;
if ((rt->rt_flags & RTF_GATEWAY) || (rt->rt_flags & RTF_LLINFO) == 0 ||
rt->rt_gateway->sa_family != AF_LINK) {
if (create)
printf("arptnew failed on %08lx\n", ntohl(addr));
return NULL;
}
return ((struct llinfo_arp *)rt->rt_llinfo);
}
int arpresolve(struct arpcom *ac, struct rtentry *rt, struct mbuf *m,
struct sockaddr *dst, uint8 *desten)
{
struct llinfo_arp *la;
struct sockaddr_dl *sdl;
if (m->m_flags & M_BCAST) {
memcpy(desten, &ether_bcast, sizeof(ether_bcast));
return 1;
}
if (m->m_flags & M_MCAST) {
ETHER_MAP_IP_MULTICAST(&SIN(dst)->sin_addr, desten);
return 1;
}
if (rt) {
la = (struct llinfo_arp*) rt->rt_llinfo;
} else {
if ((la = arplookup(SIN(dst)->sin_addr.s_addr, 1, 0)))
rt = la->la_rt;
}
if (la == NULL || rt == NULL) {
printf("arpresolve: can't allocate llinfo!\n");
m_freem(m);
return 0;
}
sdl = SDL(rt->rt_gateway);
if ((rt->rt_expire == 0 || rt->rt_expire > real_time_clock()) &&
sdl->sdl_family == AF_LINK && sdl->sdl_alen != 0) {
memcpy(desten, LLADDR(sdl), sdl->sdl_alen);
return 1;
}
if (la->la_hold) {
m_freem(la->la_hold);
}
la->la_hold = m;
if (rt->rt_expire) {
rt->rt_flags &= ~RTF_REJECT;
if (la->la_asked == 0 || rt->rt_expire != real_time_clock()) {
rt->rt_expire = real_time_clock();
if (la->la_asked++ < arp_maxtries) {
arpwhohas(ac, &(SIN(dst)->sin_addr));
} else {
rt->rt_flags |= RTF_REJECT;
rt->rt_expire += arpt_down;
la->la_asked = 0;
}
}
}
return 0;
}
static void arptfree(struct llinfo_arp *la)
{
struct rtentry *rt = la->la_rt;
struct sockaddr_dl *sdl;
if (rt == NULL)
return;
if (rt->rt_refcnt > 0 && (sdl = SDL(rt->rt_gateway)) &&
sdl->sdl_family == AF_LINK) {
sdl->sdl_alen = 0;
la->la_asked = 0;
rt->rt_flags &= ~RTF_REJECT;
return;
}
rtrequest(RTM_DELETE, rt_key(rt), NULL, rt_mask(rt), 0, NULL);
}
static void arptimer(void *data)
{
struct llinfo_arp *la = llinfo_arp.la_next;
while (la != &llinfo_arp) {
struct rtentry *rt = la->la_rt;
la = la->la_next;
if (rt->rt_expire && rt->rt_expire <= real_time_clock())
arptfree(la->la_prev);
}
return;
}
static void arprequest(struct arpcom *ac, uint32 *sip, uint32 *tip, uint8 *enaddr)
{
struct mbuf *m;
struct ether_header *eh;
struct ether_arp *ea;
struct sockaddr sa;
if ((m = m_gethdr(MT_DATA)) == NULL)
return;
m->m_len = sizeof(*ea);
m->m_pkthdr.len = sizeof(*ea);
MH_ALIGN(m, sizeof(*ea));
ea = mtod(m, struct ether_arp*);
eh = (struct ether_header*) sa.sa_data;
memset(ea, 0, sizeof(*ea));
memcpy(eh->ether_dhost, &ether_bcast, sizeof(eh->ether_dhost));
eh->ether_type = ETHERTYPE_ARP;
ea->arp_hrd = htons(ARPHRD_ETHER);
ea->arp_pro = htons(ETHERTYPE_IP);
ea->arp_hln = sizeof(ea->arp_sha);
ea->arp_pln = sizeof(ea->arp_spa);
ea->arp_op = htons(ARPOP_REQUEST);
memcpy((caddr_t)ea->arp_sha, (caddr_t)enaddr, sizeof(ea->arp_sha));
memcpy(ea->arp_spa, sip, sizeof(ea->arp_spa));
memcpy(ea->arp_tpa, tip, sizeof(ea->arp_tpa));
sa.sa_family = AF_UNSPEC;
sa.sa_len = sizeof(sa);
(*ac->ac_if.output)(&ac->ac_if, m, &sa, NULL);
}
void arpwhohas(struct arpcom *ac, struct in_addr *ia)
{
arprequest(ac, &ac->ac_ipaddr.s_addr, &ia->s_addr, ac->ac_enaddr);
}
void arp_rtrequest(int req, struct rtentry *rt, struct sockaddr *sa)
{
struct sockaddr *gate = rt->rt_gateway;
static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK};
struct llinfo_arp *la = (struct llinfo_arp*)rt->rt_llinfo;
if (rt->rt_flags & RTF_GATEWAY)
return;
switch (req) {
case RTM_ADD:
if ((rt->rt_flags & RTF_HOST) == 0 &&
SIN(rt_mask(rt))->sin_addr.s_addr != 0xffffffff)
rt->rt_flags |= RTF_CLONING;
if (rt->rt_flags & RTF_CLONING) {
rt_setgate(rt, rt_key(rt), (struct sockaddr*)&null_sdl);
gate = rt->rt_gateway;
SDL(gate)->sdl_type = rt->rt_ifp->if_type;
SDL(gate)->sdl_index = rt->rt_ifp->if_index;
rt->rt_expire = real_time_clock();
break;
}
if (rt->rt_flags & RTF_ANNOUNCE)
arprequest((struct arpcom*) rt->rt_ifp,
&SIN(rt_key(rt))->sin_addr.s_addr,
&SIN(rt_key(rt))->sin_addr.s_addr,
(uint8*)LLADDR(SDL(gate)));
case RTM_RESOLVE:
if (gate->sa_family != AF_LINK ||
gate->sa_len < sizeof(null_sdl)) {
printf("arp_rtrequest: bad gateway value!\n");
break;
}
SDL(gate)->sdl_type = rt->rt_ifp->if_type;
SDL(gate)->sdl_index = rt->rt_ifp->if_index;
if (la)
break;
R_Malloc(la, struct llinfo_arp *, sizeof(*la));
if (!la) {
printf("arp_rtrequest: malloc failed!\n");
break;
}
rt->rt_llinfo = (caddr_t)la;
arp_inuse++, arp_allocated++;
Bzero(la, sizeof(*la));
la->la_rt = rt;
rt->rt_flags |= RTF_LLINFO;
insque(la, &llinfo_arp);
if (SIN(rt_key(rt))->sin_addr.s_addr ==
(IA_SIN(rt->rt_ifa))->sin_addr.s_addr) {
rt->rt_expire = 0;
Bcopy(((struct arpcom*)rt->rt_ifp)->ac_enaddr,
LLADDR(SDL(gate)), SDL(gate)->sdl_alen = 6);
/*
XXX - add function to get the ifnet * for the loopback from the "core"
and ability to find the loopback device when added.
if (useloopback)
rt->rt_ifp = &loif;
*/
}
break;
case RTM_DELETE:
if (!la)
break;
arp_inuse--;
remque(la);
rt->rt_llinfo = NULL;
rt->rt_flags &= ~ RTF_LLINFO;
if (la->la_hold)
m_freem(la->la_hold);
Free((caddr_t)la);
}
}
static void arpinput(struct mbuf *m)
{
struct ether_arp *ea;
struct arpcom *ac = (struct arpcom *)m->m_pkthdr.rcvif;
struct ether_header *eh;
struct llinfo_arp *la = NULL;
struct in_ifaddr *ia = NULL, *maybe_ia = NULL;
struct in_addr isaddr, itaddr, myaddr;
struct rtentry *rt;
int op;
struct sockaddr_dl *sdl;
struct sockaddr sa;
#if ARP_DEBUG
dump_arp(mtod(m, void*));
#endif
if (!primary_addr)
primary_addr = get_primary_addr();
ea = mtod(m, struct ether_arp *);
op = ntohs(ea->arp_op);
memcpy(&isaddr, ea->arp_spa, sizeof(isaddr));
memcpy(&itaddr, ea->arp_tpa, sizeof(isaddr));
/* find out if it's for us... */
for (ia = primary_addr;ia ; ia = ia->ia_next)
if (ia->ia_ifp == &ac->ac_if) {
maybe_ia = ia;
if ((itaddr.s_addr == ia->ia_addr.sin_addr.s_addr) ||
(isaddr.s_addr == ia->ia_addr.sin_addr.s_addr))
break;
}
if (!maybe_ia)
goto out;
myaddr = ia ? ia->ia_addr.sin_addr : maybe_ia->ia_addr.sin_addr;
if (!memcmp(ac->ac_enaddr, ea->arp_sha, sizeof(ea->arp_sha)))
goto out;
if (!memcmp(ea->arp_sha, &ether_bcast, sizeof(ea->arp_sha))) {
printf("arp_input: ether address was broadcast for %08lx\n",
htonl(isaddr.s_addr));
goto out;
}
if (isaddr.s_addr == myaddr.s_addr) {
printf("arp_input: duplicate IP address sent from %s\n",
ether_sprintf(ea->arp_sha));
itaddr = myaddr;
goto reply;
}
la = arplookup(isaddr.s_addr, itaddr.s_addr == myaddr.s_addr, 0);
if (la && (rt = la->la_rt) && (sdl = SDL(rt->rt_gateway))) {
if (sdl->sdl_alen && memcmp(ea->arp_sha, LLADDR(sdl), sdl->sdl_alen))
printf("arp info overwritten for %08lx by %s\n",
isaddr.s_addr, ether_sprintf(ea->arp_sha));
memcpy(LLADDR(sdl), ea->arp_sha, sdl->sdl_alen = sizeof(ea->arp_sha));
if (rt->rt_expire) {
rt->rt_expire = real_time_clock() + arpt_keep;
}
rt->rt_flags &= ~RTF_REJECT;
la->la_asked = 0;
if (la->la_hold) {
(*ac->ac_if.output)(&ac->ac_if, la->la_hold, rt_key(rt), rt);
la->la_hold = NULL;
}
}
reply:
if (op != ARPOP_REQUEST) {
out:
return;
}
if (itaddr.s_addr == myaddr.s_addr) {
memcpy(ea->arp_tha, ea->arp_sha, sizeof(ea->arp_sha));
memcpy(ea->arp_sha, ac->ac_enaddr, sizeof(ea->arp_sha));
} else {
la = arplookup(itaddr.s_addr, 0, SIN_PROXY);
if (la == NULL)
goto out;
rt = la->la_rt;
memcpy(ea->arp_tha, ea->arp_sha, sizeof(ea->arp_sha));
sdl = SDL(rt->rt_gateway);
memcpy(ea->arp_sha, LLADDR(sdl), sizeof(ea->arp_sha));
}
memcpy(ea->arp_tpa, ea->arp_spa, sizeof(ea->arp_spa));
memcpy(ea->arp_spa, &itaddr, sizeof(ea->arp_spa));
ea->arp_op = htons(ARPOP_REPLY);
ea->arp_pro = htons(ETHERTYPE_IP);
eh = (struct ether_header *)sa.sa_data;
memcpy(eh->ether_dhost, ea->arp_tha, sizeof(eh->ether_dhost));
eh->ether_type = ETHERTYPE_ARP;
sa.sa_family = AF_UNSPEC;
sa.sa_len = sizeof(sa);
(*ac->ac_if.output)(&ac->ac_if, m, &sa, NULL);
return;
}
void arp_init(void)
{
llinfo_arp.la_next = llinfo_arp.la_prev = &llinfo_arp;
arptimer_id = net_add_timer(&arptimer, NULL, arpt_prune * 1000000);
}
static int ether_ioctl(struct ifnet *ifp, int cmd, caddr_t data)
{
struct arpcom *ac = (struct arpcom *)ifp;
struct ifaddr *ifa = (struct ifaddr*)data;
if (ifp->devid == -1) {
char path[PATH_MAX];
sprintf(path, "%s/%s/%d", DRIVER_DIRECTORY, ifp->name, ifp->if_unit);
ifp->devid = open(path, O_RDWR);
if (ifp->devid < 0) {
ifp->devid = -1;
return -1;
}
}
if ((ifp->if_flags & IFF_UP) == 0 &&
(ifp->rx_thread > 0 || ifp->tx_thread > 0)) {
/* shutdown our threads and remove the IFF_RUNNING flag... */
if (ifp->rx_thread > 0)
kill_thread(ifp->rx_thread);
if (ifp->tx_thread > 0)
kill_thread(ifp->tx_thread);
ifp->rx_thread = ifp->tx_thread = -1;
ifp->if_flags &= ~IFF_RUNNING;
}
ifa->ifa_rtrequest = &arp_rtrequest;
switch (cmd) {
case SIOCSIFADDR:
ifp->if_flags |= IFF_UP;
switch (ifa->ifa_addr->sa_family) {
case AF_INET:
ac->ac_ipaddr = ((struct sockaddr_in*)ifa->ifa_addr)->sin_addr;
#ifdef ETHER_DEBUG
printf("setting ether_device ip address to %08lx\n",
ntohl(((struct sockaddr_in*)ifa->ifa_addr)->sin_addr.s_addr));
#endif
break;
default:
printf("don't know how to work with address family %d\n",
ifa->ifa_addr->sa_family);
}
break;
case SIOCSIFFLAGS:
printf("ether_ioctl: SIOCSIFFLAGS\n");
break;
default:
printf("unhandled call to ethernet_ioctl\n");
}
if ((ifp->if_flags & IFF_UP) &&
(ifp->rx_thread == -1 || ifp->tx_thread == -1)) {
/* start our threads and add the IFF_RUNNING flag... */
if (ifp->rx_thread < 0)
start_rx_thread(ifp);
if (ifp->tx_thread < 0)
start_tx_thread(ifp);
ifp->if_flags |= IFF_RUNNING;
}
/* close and reset the devid so we don't try to use it again */
if ((ifp->if_flags & IFF_UP) == 0) {
close(ifp->devid);
ifp->devid = -1;
}
return 0;
}
int ether_dev_stop(ifnet *dev)
{
dev->if_flags &= ~IFF_UP;
/* should find better ways of doing this... */
kill_thread(dev->rx_thread);
kill_thread(dev->tx_thread);
return 0;
}
static int ether_init(void *cpp)
{
if (cpp)
core = cpp;
etherq = start_ifq();
ether_rxt = spawn_thread(ether_input, "ethernet_input", 50, NULL);
if (ether_rxt > 0)
resume_thread(ether_rxt);
find_devices();
memset(proto, 0, sizeof(struct protosw *) * IPPROTO_MAX);
add_protosw(proto, NET_LAYER2);
arp_init();
return 0;
}
static int ether_stop()
{
struct ether_device *dptr = ether_devices, *odev;
kill_thread(ether_rxt);
while (dptr) {
odev = dptr;
dptr = odev->next;
free(odev);
}
net_remove_timer(arptimer_id);
return 0;
}
_EXPORT struct kernel_net_module_info device_info = {
{
ETHERNET_MODULE_PATH,
0,
std_ops
},
ether_init,
ether_stop
};
static int32 std_ops(int32 op, ...)
{
switch(op) {
case B_MODULE_INIT:
get_module(CORE_MODULE_PATH, (module_info**)&core);
if (!core)
return B_ERROR;
#ifdef _KERNEL_
load_driver_symbols("ethernet");
#endif
return B_OK;
case B_MODULE_UNINIT:
return B_OK;
default:
return B_ERROR;
}
return B_OK;
}
_EXPORT module_info * modules[] = {
(module_info *) &device_info,
NULL
};
@@ -0,0 +1,2 @@
SubDir OBOS_TOP src add-ons kernel network interfaces loopback ;
@@ -0,0 +1,178 @@
/* loopback.c - loopback device
*/
#include <stdio.h>
#include <malloc.h>
#include <kernel/OS.h>
#include "sys/socket.h"
#include "protocols.h"
#include "netinet/in.h"
#include "netinet/ip.h"
#include "sys/socketvar.h"
#include "sys/protosw.h"
#include "sys/domain.h"
#include "sys/sockio.h"
#include "net_malloc.h"
#include "core_module.h"
#include "net_module.h"
#include "core_funcs.h"
#ifdef _KERNEL_
#include <KernelExport.h>
#define LOOP_MODULE_PATH "network/interfaces/loopback"
#else
#define LOOP_MODULE_PATH "interfaces/loopback"
#endif
static status_t std_ops(int32 op, ...);
static struct core_module_info *core = NULL;
static struct protosw *proto[IPPROTO_MAX];
static struct ifnet *me = NULL;
int loopback_output(struct ifnet *ifp, struct mbuf *m, struct sockaddr *sa,
struct rtentry *rt)
{
/* turn it straight back... */
/* This is lame as we should be detecting the protocol, but it gets
* us working.
* XXX - fix me!
*/
struct ip *ip = mtod(m, struct ip *);
ip->ip_dst = ip->ip_src;
ip->ip_src.s_addr = INADDR_LOOPBACK;
IFQ_ENQUEUE(ifp->rxq, m);
return 0;
}
void loopback_input(struct mbuf *buf)
{
if (!buf)
return;
buf->m_pkthdr.rcvif = me;
if (proto[IPPROTO_IP] && proto[IPPROTO_IP]->pr_input) {
proto[IPPROTO_IP]->pr_input(buf, 0);
return;
} else
printf("No input tourtine found for IP\n");
m_freem(buf);
return;
}
static int loopback_dev_stop(struct ifnet *dev)
{
if (!dev || dev->if_type != IFT_LOOP)
return EINVAL;
dev->if_flags &= ~IFF_UP;
if (dev->rx_thread > 0)
kill_thread(dev->rx_thread);
if (dev->tx_thread > 0)
kill_thread(dev->tx_thread);
dev->if_flags &= ~IFF_RUNNING;
return 0;
}
static int loopback_ioctl(struct ifnet *ifp, int cmd, caddr_t data)
{
int error = 0;
switch(cmd) {
case SIOCSIFADDR:
ifp->if_flags |= (IFF_UP | IFF_RUNNING);
if (ifp->rx_thread < 0)
start_rx_thread(ifp);
if (ifp->tx_thread < 0)
start_tx_thread(ifp);
break;
default:
error = EINVAL;
}
return error;
}
static int loopback_init(void)
{
me = (struct ifnet*)malloc(sizeof(struct ifnet));
if (!me)
return -1;
memset(me, 0, sizeof(*me));
memset(proto, 0, sizeof(struct protosw *) * IPPROTO_MAX);
me->devid = -1;
me->name = "loop";
me->if_unit = 0;
me->if_type = IFT_LOOP;
me->rx_thread = -1;
me->tx_thread = -1;
me->if_addrlen = 0;
me->if_hdrlen = 0;
me->if_flags = IFF_LOOPBACK | IFF_MULTICAST;
me->if_mtu = 16384; /* can be as large as we want */
me->input = &loopback_input;
me->output = &loopback_output;
me->stop = &loopback_dev_stop;
me->ioctl = &loopback_ioctl;
add_protosw(proto, NET_LAYER1);
if_attach(me);
return 0;
}
static int loopback_module_init(void *cpp)
{
if (cpp)
core = cpp;
loopback_init();
return 0;
}
_EXPORT struct kernel_net_module_info device_info = {
{
LOOP_MODULE_PATH,
0,
std_ops
},
loopback_module_init,
NULL,
};
static status_t std_ops(int32 op, ...)
{
switch(op) {
case B_MODULE_INIT:
get_module(CORE_MODULE_PATH, (module_info **)&core);
if (!core)
return B_ERROR;
return B_OK;
case B_MODULE_UNINIT:
break;
default:
return B_ERROR;
}
return B_OK;
}
_EXPORT module_info *modules[] = {
(module_info*) &device_info,
NULL
};
@@ -0,0 +1,8 @@
SubDir OBOS_TOP src add-ons kernel network protocols ;
SubInclude OBOS_TOP src add-ons kernel network protocols icmp ;
SubInclude OBOS_TOP src add-ons kernel network protocols ipv4 ;
SubInclude OBOS_TOP src add-ons kernel network protocols raw ;
SubInclude OBOS_TOP src add-ons kernel network protocols tcp ;
SubInclude OBOS_TOP src add-ons kernel network protocols udp ;
@@ -0,0 +1,2 @@
SubDir OBOS_TOP src add-ons kernel network protocols icmp ;
@@ -0,0 +1,503 @@
/* icmp.c
*/
#ifndef _KERNEL
#include <stdio.h>
#include <string.h>
#endif
#include "net_misc.h"
#include "sys/socket.h"
#include "netinet/in_systm.h"
#include "netinet/ip.h"
#include "netinet/ip_icmp.h"
#include "protocols.h"
#include "sys/protosw.h"
#include "sys/domain.h"
#include "netinet/icmp_var.h"
#include "netinet/in_var.h"
#include "net/if.h"
#include "core_module.h"
#include "net_module.h"
#include "core_funcs.h"
#include "raw/raw_module.h"
#include "icmp_module.h"
#include "../ipv4/ipv4_module.h"
#ifdef _KERNEL_
#include <KernelExport.h>
static status_t icmp_ops(int32 op, ...);
#else
#define icmp_ops NULL
#endif
/* private variables */
static struct core_module_info *core = NULL;
static struct raw_module_info *raw = NULL;
static struct protosw* proto[IPPROTO_MAX];
static struct in_ifaddr *ic_ifaddr = NULL;
static struct ipv4_module_info *ipm = NULL;
#ifndef _KERNEL_
static image_id ipid = -1;
#endif
static struct route icmprt;
#if SHOW_DEBUG
static void dump_icmp(struct mbuf *buf)
{
struct ip *ip = mtod(buf, struct ip *);
struct icmp *ic = (struct icmp*)((caddr_t)ip + (ip->hl * 4));
printf("ICMP: ");
switch (ic->icmp_type) {
case ICMP_ECHORQST:
printf ("Echo request\n");
break;
case ICMP_ECHORPLY:
printf("echo reply\n");
break;
default:
printf("?? type = %d\n", ic->type);
}
}
#endif
static void icmp_send(struct mbuf *m, struct mbuf *opts)
{
struct ip *ip = mtod(m, struct ip*);
int hlen;
struct icmp *icp;
hlen = ip->ip_hl << 2;
m->m_data += hlen;
m->m_len -= hlen;
icp = mtod(m, struct icmp *);
icp->icmp_cksum = 0;
icp->icmp_cksum = in_cksum(m, ip->ip_len - hlen, 0);
m->m_data -= hlen;
m->m_len += hlen;
proto[IPPROTO_IP]->pr_output(m, opts, NULL, 0, NULL);
}
static void icmp_reflect(struct mbuf *m)
{
struct ip *ip = mtod(m, struct ip*);
struct in_ifaddr *ia;
struct in_addr t;
struct mbuf *opts = NULL;
int optlen = (ip->ip_hl << 2) - sizeof(struct ip);
struct sockaddr_in icmpdst;
if (!ic_ifaddr) {
ic_ifaddr = get_primary_addr();
if (!ic_ifaddr) {
printf("icmp_reflect: no interfaces available (ic_ifaddr == NULL)\n");
m_freem(m);
return;
}
}
if (!in_canforward(ip->ip_src) &&
((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) !=
(IN_LOOPBACKNET << IN_CLASSA_NSHIFT))) {
printf("icmp_reflect: can't forward packet!\n");
m_freem(m);
goto done;
}
t = ip->ip_dst;
ip->ip_dst = ip->ip_src;
for (ia = ic_ifaddr; ia; ia = ia->ia_next) {
if (t.s_addr == IA_SIN(ia)->sin_addr.s_addr)
break;
if ((ia->ia_ifp->if_flags & IFF_BROADCAST) &&
t.s_addr == satosin(&ia->ia_broadaddr)->sin_addr.s_addr)
break;
}
icmpdst.sin_addr = t;
if (ia == NULL)
ia = (struct in_ifaddr*)ifaof_ifpforaddr((struct sockaddr*)&icmpdst,
m->m_pkthdr.rcvif);
if (ia == NULL)
ia = in_ifaddr;
t = IA_SIN(ia)->sin_addr;
ip->ip_src = t;
ip->ip_ttl = MAXTTL;
if (optlen > 0) {
uint8 *cp;
int opt, cnt;
uint len;
cp = (uint8*)(ip + 1);
if ((opts = ipm->srcroute()) == 0 &&
(opts = m_gethdr(MT_HEADER))) {
opts->m_len = sizeof(struct in_addr);
mtod(opts, struct in_addr*)->s_addr = 0;
}
if (opts) {
for (cnt = optlen; cnt > 0; cnt -= len, cp+= len) {
opt = cp[IPOPT_OPTVAL];
if (opt == IPOPT_EOL)
break;
if (opt == IPOPT_NOP)
len = 1;
else {
len = cp[IPOPT_OLEN];
if (len <= 0 || len > cnt)
break;
}
if (opt == IPOPT_RR || opt == IPOPT_TS ||
opt == IPOPT_SECURITY) {
memcpy((void*)(mtod(opts, char*) + opts->m_len),
(void*)cp, len);
opts->m_len += len;
}
}
if ((cnt = opts->m_len % 4)) {
for (; cnt < 4; cnt++) {
*(mtod(opts, char*) + opts->m_len) = IPOPT_EOL;
opts->m_len++;
}
}
}
ip->ip_len -= optlen;
ip->ip_hl = sizeof(struct ip) >> 2;
m->m_len -= optlen;
if (m->m_flags & M_PKTHDR)
m->m_pkthdr.len -= optlen;
optlen += sizeof(struct ip);
memcpy((void*)(ip + 1), (void*)(ip + optlen),
(uint)(m->m_len - sizeof(struct ip)));
}
m->m_flags &= ~(M_BCAST | M_MCAST);
icmp_send(m, opts);
done:
if (opts)
m_free(opts);
}
void icmp_input(struct mbuf *buf, int hdrlen)
{
struct ip *ip = mtod(buf, struct ip *);
struct icmp *ic;
int icl = ip->ip_len;
int i;
uint16 rv;
int code;
struct sockaddr_in icmpsrc = {sizeof(struct sockaddr_in), AF_INET};
if (icl < ICMP_MINLEN) {
icmpstat.icps_tooshort++;
goto freeit;
}
#if SHOW_DEBUG
dump_icmp(buf);
#endif
i = hdrlen + min(icl, ICMP_ADVLENMIN);
if (buf->m_len < i && (buf = m_pullup(buf, i)) == NULL) {
icmpstat.icps_tooshort++;
return;
}
ip = mtod(buf, struct ip*);
buf->m_len -= hdrlen;
buf->m_data += hdrlen;
ic = mtod(buf, struct icmp*);
if ((rv = in_cksum(buf, icl, 0)) != 0) {
printf("icmp_input: checksum failed over %d bytes (%d)!\n", icl, rv);
icmpstat.icps_checksum++;
goto freeit;
}
buf->m_len += hdrlen;
buf->m_data -= hdrlen;
if (ic->icmp_type > ICMP_MAXTYPE)
goto raw;
icmpstat.icps_inhist[ic->icmp_type]++;
code = ic->icmp_code;
switch (ic->icmp_type) {
case ICMP_UNREACH:
switch (code) {
case ICMP_UNREACH_NET:
case ICMP_UNREACH_HOST:
case ICMP_UNREACH_PROTOCOL:
case ICMP_UNREACH_PORT:
case ICMP_UNREACH_SRCFAIL:
code += PRC_UNREACH_NET;
break;
case ICMP_UNREACH_NEEDFRAG:
code = PRC_MSGSIZE;
break;
case ICMP_UNREACH_NET_UNKNOWN:
case ICMP_UNREACH_NET_PROHIB:
case ICMP_UNREACH_TOSNET:
code = PRC_UNREACH_NET;
break;
case ICMP_UNREACH_HOST_UNKNOWN:
case ICMP_UNREACH_ISOLATED:
case ICMP_UNREACH_HOST_PROHIB:
case ICMP_UNREACH_TOSHOST:
code = PRC_UNREACH_HOST;
break;
default:
goto badcode;
}
goto deliver;
case ICMP_TIMXCEED:
if (code > 1)
goto badcode;
code += PRC_TIMXCEED_INTRANS;
goto deliver;
case ICMP_PARAMPROB:
if (code > 1)
goto badcode;
code = PRC_PARAMPROB;
goto deliver;
case ICMP_SOURCEQUENCH:
if (code)
goto badcode;
code = PRC_QUENCH;
deliver:
if (icl < ICMP_ADVLENMIN || icl < ICMP_ADVLEN(ic) ||
(ic->icmp_ip.ip_hl < sizeof(struct ip) >> 2)) {
icmpstat.icps_badlen++;
goto freeit;
}
ic->icmp_ip.ip_len = htons(ic->icmp_ip.ip_len);
icmpsrc.sin_addr = ic->icmp_ip.ip_dst;
if (proto[ic->icmp_ip.ip_p]->pr_ctlinput)
proto[ic->icmp_ip.ip_p]->pr_ctlinput(code,
(struct sockaddr*)&icmpsrc, (void*)&ic->icmp_ip);
break;
badcode:
icmpstat.icps_badcode++;
break;
case ICMP_ECHO: {
ic->icmp_type = ICMP_ECHOREPLY;
ip->ip_len += hdrlen;
icmpstat.icps_reflect++;
icmpstat.icps_outhist[ic->icmp_type]++;
icmp_reflect(buf);
return;
break;
}
case ICMP_ECHOREPLY:
break;
default:
break;
}
raw:
if (raw)
raw->input(buf, 0);
return;
freeit:
m_freem(buf);
return;
}
void icmp_error(struct mbuf *n, int type, int code, n_long dest,
struct ifnet *destifp)
{
struct ip *oip = mtod(n, struct ip*), *nip;
uint oiplen = oip->ip_hl << 2;
struct icmp *icp;
struct mbuf *m;
uint icmplen;
if (type != ICMP_REDIRECT)
icmpstat.icps_error++;
if (oip->ip_off & ~(IP_MF | IP_DF))
goto freeit;
if (oip->ip_p == IPPROTO_ICMP && type != ICMP_REDIRECT &&
n->m_len >= oiplen + ICMP_MINLEN &&
ICMP_INFOTYPE(((struct icmp*)((void*)(oip + oiplen)))->icmp_type)) {
icmpstat.icps_oldicmp++;
goto freeit;
}
/* don't send icmp errors in response to multi or broad cast */
if (n->m_flags & (M_BCAST | M_MCAST))
goto freeit;
m = m_gethdr(MT_HEADER);
if (!m)
goto freeit;
icmplen = oiplen + min(8, oip->ip_len);
m->m_len = icmplen + ICMP_MINLEN;
MH_ALIGN(m, m->m_len);
icp = mtod(m, struct icmp*);
if ((uint)type > ICMP_MAXTYPE) {
/* PANIC */
printf("PANIC: icmp_error! type outside of range\n");
return;
}
icmpstat.icps_outhist[type]++;
icp->icmp_type = type;
if (type == ICMP_REDIRECT)
icp->icmp_gwaddr.s_addr = dest;
else {
icp->icmp_void = 0;
if (type == ICMP_PARAMPROB) {
icp->icmp_pptr = code;
} else if (type == ICMP_UNREACH &&
code == ICMP_UNREACH_NEEDFRAG &&
destifp) {
icp->icmp_nextmtu = htons(destifp->if_mtu);
}
}
icp->icmp_code = code;
memcpy((void*)&icp->icmp_ip, (void*)oip, icmplen);
nip = &icp->icmp_ip;
nip->ip_len = htons((nip->ip_len + oiplen));
if (m->m_data - sizeof(struct ip) < m->m_pktdat) {
/* PANIC */
printf("PANIC: icmp_error: icmp len\n");
return;
}
m->m_data -= sizeof(struct ip);
m->m_len += sizeof(struct ip);
m->m_pkthdr.len = m->m_len;
m->m_pkthdr.rcvif = n->m_pkthdr.rcvif;
nip = mtod(m, struct ip*);
memcpy((void*)nip, (void*)oip, sizeof(struct ip));
nip->ip_len = m->m_len;
nip->ip_hl = sizeof(struct ip) >> 2;
nip->ip_p = IPPROTO_ICMP;
nip->ip_tos = 0;
icmp_reflect(m);
freeit:
m_freem(n);
}
static void icmp_init(void)
{
memset(&icmprt, 0, sizeof(icmprt));
memset(proto, 0, sizeof(struct protosw *) * IPPROTO_MAX);
add_protosw(proto, NET_LAYER2);
#ifdef _KERNEL
if (!raw)
get_module(RAW_MODULE_PATH, (module_info**)&raw);
#endif
ic_ifaddr = get_primary_addr();
}
struct protosw my_proto = {
"ICMP (v4)",
ICMP_MODULE_PATH,
0,
NULL,
IPPROTO_ICMP,
PR_ATOMIC | PR_ADDR,
NET_LAYER2,
&icmp_init,
&icmp_input,
NULL, /* pr_output, */
NULL,
NULL, /* pr_sysctl */
NULL,
NULL, /* pr_ctloutput */
NULL,
NULL
};
static int icmp_protocol_init(void *cpp)
{
/* we will never call this with anything but NULL when in kernel,
* so this should be safe.
*/
if (cpp)
core = (struct core_module_info *)cpp;
add_domain(NULL, AF_INET);
add_protocol(&my_proto, AF_INET);
#ifndef _KERNEL_
if (!ipm) {
char path[PATH_MAX];
getcwd(path, PATH_MAX);
strcat(path, "/" IPV4_MODULE_PATH);
ipid = load_add_on(path);
if (ipid > 0) {
status_t rv = get_image_symbol(ipid, "protocol_info",
B_SYMBOL_TYPE_DATA, (void**)&ipm);
if (rv < 0) {
printf("Failed to get access to IPv4 information!\n");
return -1;
}
} else {
printf("Failed to load the IPv4 module...\n");
return -1;
}
ipm->set_core(cpp);
}
#else
if (!ipm)
get_module(IPV4_MODULE_PATH, (module_info**)&ipm);
#endif
return 0;
}
static int icmp_protocol_stop(void)
{
remove_protocol(&my_proto);
remove_domain(AF_INET);
return 0;
}
#ifndef _KERNEL_
void set_core(struct core_module_info *cp)
{
core = cp;
}
#endif
_EXPORT struct icmp_module_info protocol_info = {
{
{
ICMP_MODULE_PATH,
0,
icmp_ops
},
icmp_protocol_init,
icmp_protocol_stop
},
#ifndef _KERNEL_
set_core,
#endif
icmp_error
};
#ifdef _KERNEL_
static status_t icmp_ops(int32 op, ...)
{
switch(op) {
case B_MODULE_INIT:
if (!core)
get_module(CORE_MODULE_PATH, (module_info**)&core);
if (!core)
return B_ERROR;
load_driver_symbols("icmp");
return B_OK;
case B_MODULE_UNINIT:
break;
default:
return B_ERROR;
}
return B_OK;
}
_EXPORT module_info *modules[] = {
(module_info *)&protocol_info,
NULL
};
#endif
@@ -0,0 +1,2 @@
SubDir OBOS_TOP src add-ons kernel network protocols ipv4 ;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,378 @@
/* raw.c */
#ifndef _KERNEL_
#include <stdio.h>
#include <string.h>
#endif
#include "sys/protosw.h"
#include "sys/domain.h"
#include "sys/socket.h"
#include "netinet/in_pcb.h"
#include "netinet/in.h"
#include "netinet/in_var.h"
#include "netinet/ip_var.h"
#include "core_module.h"
#include "net_module.h"
#include "core_funcs.h"
#include "raw/raw_module.h"
#include "ipv4/ipv4_module.h"
#ifdef _KERNEL_
#include <KernelExport.h>
static status_t raw_ops(int32 op, ...);
#else /* _KERNEL_ */
#define raw_ops NULL
static image_id ipid;
#endif
static struct core_module_info *core = NULL;
static struct ipv4_module_info *ipm = NULL;
static struct inpcb rawinpcb;
static struct sockaddr_in ripsrc;
static int rip_sendspace = 8192;
static int rip_recvspace = 8192;
void rip_init(void)
{
rawinpcb.inp_next = rawinpcb.inp_prev = &rawinpcb;
memset(&ripsrc, 0, sizeof(ripsrc));
ripsrc.sin_family = AF_INET;
ripsrc.sin_len = sizeof(ripsrc);
}
void rip_input(struct mbuf *m, int hdrlen)
{
struct ip *ip = mtod(m, struct ip*);
struct inpcb *inp;
struct socket *last = NULL;
ripsrc.sin_addr = ip->ip_src;
for (inp = rawinpcb.inp_next; inp != &rawinpcb; inp=inp->inp_next) {
if (inp->inp_ip.ip_p && inp->inp_ip.ip_p != ip->ip_p)
continue;
if (inp->laddr.s_addr && inp->laddr.s_addr == ip->ip_dst.s_addr)
continue;
if (inp->faddr.s_addr && inp->faddr.s_addr == ip->ip_src.s_addr)
continue;
if (last) {
struct mbuf *n;
if ((n = m_copym(m, 0, (int)M_COPYALL))) {
if (sbappendaddr(&last->so_rcv, (struct sockaddr*)&ripsrc,
n, NULL) == 0)
m_freem(n);
else
sorwakeup(last);
}
}
last = inp->inp_socket;
}
if (last) {
if (sbappendaddr(&last->so_rcv, (struct sockaddr*)&ripsrc,
m, NULL) == 0)
m_freem(m);
else
sorwakeup(last);
} else {
m_freem(m);
ipstat.ips_noproto++;
ipstat.ips_delivered--;
}
return;
}
int rip_output(struct mbuf *m, struct socket *so, uint32 dst)
{
struct ip *ip;
struct inpcb *inp = sotoinpcb(so);
struct mbuf *opts;
int flags = (so->so_options & SO_DONTROUTE) | IP_ALLOWBROADCAST;
if ((inp->inp_flags & INP_HDRINCL) == 0) {
M_PREPEND(m, sizeof(struct ip));
ip = mtod(m, struct ip *);
ip->ip_p = inp->inp_ip.ip_p;
ip->ip_len = m->m_pkthdr.len;
ip->ip_src = inp->laddr;
ip->ip_dst.s_addr = dst;
ip->ip_ttl = MAXTTL;
opts = inp->inp_options;
ip->ip_off = 0;
ip->ip_tos = 0;
} else {
ip = mtod(m, struct ip *);
/* ip_output relies on having the ip->ip_len in host
* order...this is lame... */
ip->ip_len = ntohs(ip->ip_len);
if (ip->ip_id == 0)
if (ipm)
ip->ip_id = htons(ipm->ip_id());
opts = NULL;
flags |= IP_RAWOUTPUT;
ipstat.ips_rawout++;
}
if (ipm) {
return ipm->output(m, opts, &inp->inp_route, flags, NULL);
}
/* XXX - last arg should be inp->inp_moptions when we have multicast */
return 0;
}
int rip_userreq(struct socket *so, int req, struct mbuf *m, struct mbuf *nam,
struct mbuf *control)
{
int error = 0;
struct inpcb *inp = sotoinpcb(so);
struct ifnet *interfaces = get_interfaces();
switch(req) {
case PRU_ATTACH:
if (inp) {
printf("Trying to attach to a socket already attached!\n");
return EINVAL;
}
if ((error = soreserve(so, rip_sendspace, rip_recvspace)) ||
(error = in_pcballoc(so, &rawinpcb)))
break;
inp = (struct inpcb*)so->so_pcb;
inp->inp_ip.ip_p = (int)nam;
break;
case PRU_DISCONNECT:
if ((so->so_state & SS_ISCONNECTED) == 0) {
error = ENOTCONN;
break;
}
case PRU_ABORT:
soisdisconnected(so);
case PRU_DETACH:
if (inp == NULL) {
printf("Can't detach from NULL protocol block!\n");
error = EINVAL;
break;
}
in_pcbdetach(inp);
break;
case PRU_SEND: {
uint32 dst;
if ((so->so_state & SS_ISCONNECTED)) {
if (nam) {
error = EISCONN;
break;
}
dst = inp->faddr.s_addr;
} else {
if (!nam) {
error = ENOTCONN;
break;
}
dst = mtod(nam, struct sockaddr_in *)->sin_addr.s_addr;
}
error = rip_output(m, so, dst);
m = NULL;
break;
}
case PRU_BIND: {
struct sockaddr_in *addr = mtod(nam, struct sockaddr_in *);
if (nam->m_len != sizeof(*addr)) {
error = EINVAL;
break;
}
if ((interfaces) ||
((addr->sin_family != AF_INET) &&
(addr->sin_family != AF_IMPLINK)) ||
(addr->sin_addr.s_addr &&
ifa_ifwithaddr((struct sockaddr*)addr) == 0)) {
error = EADDRNOTAVAIL;
break;
}
inp->laddr = addr->sin_addr;
break;
}
case PRU_CONNECT: {
struct sockaddr_in *addr = mtod(nam, struct sockaddr_in *);
if (nam->m_len != sizeof(*addr)) {
error = EINVAL;
break;
}
if ((interfaces == NULL)) {
error = EADDRNOTAVAIL;
break;
}
if ((addr->sin_family != AF_INET) &&
(addr->sin_family != AF_IMPLINK)) {
error = EAFNOSUPPORT;
break;
}
inp->faddr = addr->sin_addr;
soisconnected(so);
break;
}
case PRU_CONNECT2:
error = EOPNOTSUPP;
break;
case PRU_SHUTDOWN:
socantsendmore(so);
break;
case PRU_RCVOOB:
case PRU_RCVD:
case PRU_LISTEN:
case PRU_ACCEPT:
case PRU_SENDOOB:
error = EINVAL;//EOPNOTSUPP;
break;
/* add remaining cases */
}
return error;
}
int rip_ctloutput(int op, struct socket *so, int level,
int optnum, struct mbuf **m)
{
struct inpcb *inp = sotoinpcb(so);
if (level != IPPROTO_IP)
return EINVAL;
switch (optnum) {
case IP_HDRINCL:
if (op == PRCO_SETOPT || op == PRCO_GETOPT) {
if (m == NULL || *m == NULL || (*m)->m_len < sizeof(int))
return EINVAL;
if (op == PRCO_SETOPT) {
if (*mtod(*m, int*))
inp->inp_flags |= INP_HDRINCL;
else
inp->inp_flags &= ~INP_HDRINCL;
m_free(*m);
} else {
(*m)->m_len = sizeof(int);
*mtod(*m, int*) = inp->inp_flags & INP_HDRINCL;
}
return 0;
}
break;
/* XXX - Add other options here */
}
#ifdef _KERNEL_
return ipm->ctloutput(op, so, level, optnum, m);
#else
/* XXX - get this working for app...? */
return 0;
#endif
}
static struct protosw my_protocol = {
"Raw IP module",
RAW_MODULE_PATH,
SOCK_RAW,
NULL,
0,
PR_ATOMIC | PR_ADDR,
NET_LAYER4,
&rip_init,
&rip_input,
NULL,
&rip_userreq,
NULL, /* pr_sysctl */
NULL,
&rip_ctloutput,
NULL,
NULL
};
static int raw_module_init(void *cpp)
{
if (cpp)
core = cpp;
add_domain(NULL, AF_INET);
add_protocol(&my_protocol, AF_INET);
#ifndef _KERNEL_
if (!ipm) {
char path[PATH_MAX];
getcwd(path, PATH_MAX);
strcat(path, "/" IPV4_MODULE_PATH);
ipid = load_add_on(path);
if (ipid > 0) {
status_t rv = get_image_symbol(ipid, "protocol_info",
B_SYMBOL_TYPE_DATA, (void**)&ipm);
if (rv < 0) {
printf("Failed to get access to IPv4 information!\n");
return -1;
}
ipm->set_core(cpp);
} else {
printf("Failed to load the IPv4 module...%ld [%s]\n",
ipid, strerror(ipid));
return -1;
}
}
#else
if (!ipm)
get_module(IPV4_MODULE_PATH, (module_info**)&ipm);
#endif
return 0;
}
static int raw_module_stop(void)
{
#ifndef _KERNEL_
unload_add_on(ipid);
#else
put_module(IPV4_MODULE_PATH);
#endif
remove_protocol(&my_protocol);
remove_domain(AF_INET);
return 0;
}
_EXPORT struct raw_module_info protocol_info = {
{
{
RAW_MODULE_PATH,
0,
raw_ops
},
raw_module_init,
raw_module_stop
},
&rip_input
};
#ifdef _KERNEL_
static status_t raw_ops(int32 op, ...)
{
switch(op) {
case B_MODULE_INIT:
get_module(CORE_MODULE_PATH, (module_info**)&core);
if (!core)
return B_ERROR;
return B_OK;
case B_MODULE_UNINIT:
break;
default:
return B_ERROR;
}
return B_OK;
}
_EXPORT module_info *modules[] = {
(module_info*)&protocol_info,
NULL
};
#endif
@@ -0,0 +1,146 @@
/*
* Arc4 random number generator for OpenBSD.
* Copyright 1996 David Mazieres <[email protected]>.
*
* Modification and redistribution in source and binary forms is
* permitted provided that due credit is given to the author and the
* OpenBSD project by leaving this copyright notice intact.
*/
/*
* This code is derived from section 17.1 of Applied Cryptography,
* second edition, which describes a stream cipher allegedly
* compatible with RSA Labs "RC4" cipher (the actual description of
* which is a trade secret). The same algorithm is used as a stream
* cipher called "arcfour" in Tatu Ylonen's ssh package.
*
* Here the stream cipher has been modified always to include the time
* when initializing the state. That makes it impossible to
* regenerate the same random sequence twice, so this can't be used
* for encryption, but will generate good random numbers.
*
* RC4 is a registered trademark of RSA Laboratories.
*/
#include <kernel/OS.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/time.h>
#ifdef __GNUC__
#define inline __inline
#else /* !__GNUC__ */
#define inline
#endif /* !__GNUC__ */
struct arc4_stream {
uint8 i;
uint8 j;
uint8 s[256];
};
int rs_initialized;
static struct arc4_stream rs;
static inline void arc4_init(struct arc4_stream *as)
{
int n;
for (n = 0; n < 256; n++)
as->s[n] = n;
as->i = 0;
as->j = 0;
}
static inline void arc4_addrandom(struct arc4_stream *as,
u_char *dat, int datlen)
{
int n;
uint8 si;
as->i--;
for (n = 0; n < 256; n++) {
as->i = (as->i + 1);
si = as->s[as->i];
as->j = (as->j + si + dat[n % datlen]);
as->s[as->i] = as->s[as->j];
as->s[as->j] = si;
}
as->j = as->i;
}
static void arc4_stir(struct arc4_stream *as)
{
int fd;
struct {
struct timeval tv;
u_int rnd[(128 - sizeof(struct timeval)) / sizeof(u_int)];
} rdat;
gettimeofday(&rdat.tv, NULL);
fd = open("/dev/arandom", O_RDONLY);
if (fd != -1) {
read(fd, rdat.rnd, sizeof(rdat.rnd));
close(fd);
}
/* fd < 0 or failed sysctl ? Ah, what the heck. We'll just take
* whatever was on the stack... */
arc4_addrandom(as, (void *) &rdat, sizeof(rdat));
}
static inline uint8 arc4_getbyte(struct arc4_stream *as)
{
uint8 si, sj;
as->i = (as->i + 1);
si = as->s[as->i];
as->j = (as->j + si);
sj = as->s[as->j];
as->s[as->i] = sj;
as->s[as->j] = si;
return (as->s[(si + sj) & 0xff]);
}
static inline uint32 arc4_getword(struct arc4_stream *as)
{
uint32 val;
val = arc4_getbyte(as) << 24;
val |= arc4_getbyte(as) << 16;
val |= arc4_getbyte(as) << 8;
val |= arc4_getbyte(as);
return val;
}
void arc4random_stir(void)
{
if (!rs_initialized) {
arc4_init(&rs);
rs_initialized = 1;
}
arc4_stir(&rs);
}
void arc4random_addrandom(u_char *dat, int datlen)
{
if (!rs_initialized)
arc4random_stir();
arc4_addrandom(&rs, dat, datlen);
}
uint32 arc4random()
{
if (!rs_initialized)
arc4random_stir();
return arc4_getword(&rs);
}
@@ -0,0 +1,600 @@
/* udp.c
*/
#ifndef _KERNEL_
#include <stdio.h>
#include <string.h>
#endif
#include "pools.h"
#include "net_misc.h"
#include "protocols.h"
#include "netinet/in_systm.h"
#include "netinet/in_var.h"
#include "netinet/in_pcb.h"
#include "netinet/ip.h"
#include "sys/domain.h"
#include "sys/protosw.h"
#include "netinet/ip_var.h"
#include "netinet/tcp.h"
#include "netinet/tcp_timer.h"
#include "netinet/tcp_fsm.h"
#include "netinet/tcp_seq.h"
#include "netinet/tcp_var.h"
#include "netinet/tcpip.h"
#include "core_module.h"
#include "net_module.h"
#include "core_funcs.h"
#include "ipv4/ipv4_module.h"
#include "net_timer.h"
#ifdef _KERNEL_
#include <KernelExport.h>
#define TCP_MODULE_PATH "network/protocol/tcp"
static status_t tcp_ops(int32 op, ...);
#else /* _KERNEL_ */
#define tcp_ops NULL
#define TCP_MODULE_PATH "modules/protocol/tcp"
static image_id ipid = -1;
#endif
struct core_module_info *core = NULL;
struct ipv4_module_info *ipm = NULL;
/* Declaration as we don't have it natively... */
uint32 arc4random();
struct protosw *proto[IPPROTO_MAX];
struct pool_ctl *tcppool = NULL;
/* patchable/settable parameters for tcp */
int tcp_mssdflt = TCP_MSS;
int tcp_rttdflt = TCPTV_SRTTDFLT / PR_SLOWHZ;
static net_timer_id slowtim;
static net_timer_id fasttim;
struct inpcb *tcp_last_inpcb = NULL;
static uint32 tcp_sendspace = 8192; /* size of send buffer */
static uint32 tcp_recvspace = 8192; /* size of recieve buffer */
void tcp_init(void)
{
tcp_now = arc4random() / 2;
tcp_iss = 1;
tcb.inp_next = tcb.inp_prev = &tcb;
if (max_protohdr < sizeof(struct tcpiphdr))
max_protohdr = sizeof(struct tcpiphdr);
memset(&tcpstat, 0, sizeof(struct tcpstat));
memset(proto, 0, sizeof(struct protosw *) * IPPROTO_MAX);
add_protosw(proto, NET_LAYER4);
if (!tcppool)
pool_init(&tcppool, sizeof(struct tcpcb));
/* Add timers... */
/* Assuming we're using usecs, then we call PR_SLOWHZ per sec
* which is 1,000,000 / PR_SLOWHZ
*/
slowtim = net_add_timer(&tcp_slowtimer, NULL, 1000000 / PR_SLOWHZ);
fasttim = net_add_timer(&tcp_fasttimer, NULL, 1000000 / PR_FASTHZ);
}
struct tcpiphdr *tcp_template(struct tcpcb *tp)
{
struct inpcb *inp = tp->t_inpcb;
struct mbuf *m;
struct tcpiphdr *n = NULL;
if ((n = tp->t_template) == NULL) {
m = m_get(MT_HEADER);
if (m == NULL)
return NULL;
m->m_len = sizeof(struct tcpiphdr);
n = mtod(m, struct tcpiphdr*);
}
/* ??? maybe we should just memset 0 and then fill in what we need? */
n->ti_next = n->ti_prev = NULL;
n->ti_x1 = 0;
n->ti_pr = IPPROTO_TCP;
n->ti_len = htons(sizeof(struct tcpiphdr) - sizeof(struct ip));
n->ti_src = inp->laddr;
n->ti_dst = inp->faddr;
n->ti_sport = inp->lport;
n->ti_dport = inp->fport;
n->ti_seq = 0;
n->ti_ack = 0;
n->ti_x2 = 0;
n->ti_off = 5;
n->ti_flags = 0;
n->ti_win = 0;
n->ti_sum = 0;
n->ti_urp = 0;
return n;
}
struct tcpcb *tcp_close(struct tcpcb *tp)
{
struct tcpiphdr *t;
struct inpcb *inp = tp->t_inpcb;
struct socket *so = inp->inp_socket;
struct mbuf *m;
struct rtentry *rt;
/* did we send enough data to get some meaningful iformation?
* If we did save it in the routing entry.
* We define enough as being the sendpipesize (default 8k) x 16
* which should give us 16 rtt samples, assuming of course we only
* have one sample per frame.
* 16 samples is enough for the srtt filter to converge to within 5%
* of the correct value.
*
* We don't however update the default route or anything else that the
* user has locked.
*/
if (SEQ_LT(tp->iss + so->so_snd.sb_hiwat * 16, tp->snd_max) &&
(rt = inp->inp_route.ro_rt) &&
((struct sockaddr_in*)rt_key(rt))->sin_addr.s_addr != INADDR_ANY) {
uint32 i;
if ((rt->rt_rmx.rmx_locks & RTV_RTT) == 0) {
i = tp->t_srtt * (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTT_SCALE));
if (rt->rt_rmx.rmx_rtt && i)
/*
* update this value with half the old and new values,
* converting scale.
*/
rt->rt_rmx.rmx_rtt = (rt->rt_rmx.rmx_rtt + i) / 2;
else
rt->rt_rmx.rmx_rtt = i;
}
if ((rt->rt_rmx.rmx_locks & RTV_RTTVAR) == 0) {
i = tp->t_rttvar * (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTTVAR_SCALE));
if (rt->rt_rmx.rmx_rttvar && i)
rt->rt_rmx.rmx_rttvar = (rt->rt_rmx.rmx_rttvar + i) / 2;
else
rt->rt_rmx.rmx_rttvar = i;
}
/* update the pipelimit (ssthresh) */
if ((rt->rt_rmx.rmx_locks & RTV_SSTHRESH) == 0 &&
((i = tp->snd_ssthresh) && (rt->rt_rmx.rmx_ssthresh ||
i < (rt->rt_rmx.rmx_sendpipe / 2)))) {
/* convert the limit from user data bytes to
* packets and then to packet data bytes
*/
i = (i + tp->t_maxseg / 2) / tp->t_maxseg;
if (i < 2)
i = 2;
i *= (u_long)(tp->t_maxseg + sizeof(struct tcpiphdr));
if (rt->rt_rmx.rmx_ssthresh)
rt->rt_rmx.rmx_ssthresh = (rt->rt_rmx.rmx_ssthresh + i) / 2;
else
rt->rt_rmx.rmx_ssthresh = i;
}
}
/* free our reassembly queue */
t = tp->seg_next;
while (t != (struct tcpiphdr*)tp) {
t = (struct tcpiphdr*)t->ti_next;
m = REASS_MBUF((struct tcpiphdr*)t->ti_prev);
remque(t->ti_prev);
m_freem(m);
}
if (tp->t_template)
(void)m_free(dtom(tp->t_template));
pool_put(tcppool, tp);
inp->inp_ppcb = NULL;
soisdisconnected(so);
if (inp == tcp_last_inpcb)
tcp_last_inpcb = &tcb;
in_pcbdetach(inp);
tcpstat.tcps_closed++;
return NULL;
}
struct tcpcb *tcp_drop(struct tcpcb *tp, int error)
{
struct socket *so = tp->t_inpcb->inp_socket;
if (TCPS_HAVERCVDSYN(tp->t_state)) {
tp->t_state = TCPS_CLOSED;
(void) tcp_output(tp);
tcpstat.tcps_drops++;
} else
tcpstat.tcps_conndrops++;
if (error == ETIMEDOUT && tp->t_softerror)
error = tp->t_softerror;
so->so_error = error;
return tcp_close(tp);
}
void tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m,
tcp_seq ack, tcp_seq seq, int flags)
{
int tlen;
int win = 0;
struct route *ro = NULL;
if (tp) {
win = sbspace(&tp->t_inpcb->inp_socket->so_rcv);
ro = &tp->t_inpcb->inp_route;
}
if (m == NULL) {
m = m_gethdr(MT_HEADER);
if (!m)
return;
tlen = 0;
m->m_data += max_linkhdr;
*mtod(m, struct tcpiphdr*) = *ti;
ti = mtod(m, struct tcpiphdr*);
flags = TH_ACK;
} else {
m_freem(m->m_next);
m->m_next = NULL;
m->m_data = (caddr_t) ti;
m->m_len = sizeof(struct tcpiphdr);
tlen = 0;
#define xchng(a,b,type) { type t; t=a; a=b; b= t; }
xchng(ti->ti_dst.s_addr, ti->ti_src.s_addr, uint32);
xchng(ti->ti_dport, ti->ti_sport, uint16);
#undef xchng
}
ti->ti_len = htons((uint16)(sizeof(struct tcphdr) + tlen));
tlen += sizeof(struct tcpiphdr);
m->m_len = tlen;
m->m_pkthdr.len = tlen;
m->m_pkthdr.rcvif = NULL;
ti->ti_next = ti->ti_prev = NULL;
ti->ti_x1 = 0;
ti->ti_seq = htonl(seq);
ti->ti_ack = htonl(ack);
ti->ti_x2 = 0;
ti->ti_off = sizeof(struct tcphdr) >> 2;
ti->ti_flags = flags;
if (tp)
ti->ti_win = htons((uint16)(win >> tp->rcv_scale));
else
ti->ti_win = htons((uint16)win);
ti->ti_urp = 0;
ti->ti_sum = 0;
ti->ti_sum = in_cksum(m, tlen, 0);
((struct ip*)ti)->ip_len = tlen;
((struct ip*)ti)->ip_ttl = 64;/* XXX - ip_defttl; */
ipm->output(m, NULL, ro, 0, NULL);
}
struct tcpcb *tcp_usrclosed(struct tcpcb *tp)
{
switch(tp->t_state) {
case TCPS_CLOSED:
case TCPS_LISTEN:
case TCPS_SYN_SENT:
tp->t_state = TCPS_CLOSED;
tp = tcp_close(tp);
break;
case TCPS_SYN_RECEIVED:
case TCPS_ESTABLISHED:
tp->t_state = TCPS_FIN_WAIT_1;
break;
case TCPS_CLOSE_WAIT:
tp->t_state = TCPS_LAST_ACK;
break;
}
if (tp && tp->t_state >= TCPS_FIN_WAIT_2)
soisdisconnected(tp->t_inpcb->inp_socket);
return tp;
}
static struct tcpcb *tcp_disconnect(struct tcpcb *tp)
{
struct socket *so = tp->t_inpcb->inp_socket;
if (tp->t_state < TCPS_ESTABLISHED)
tp = tcp_close(tp);
else if ((so->so_options & SO_LINGER) && so->so_linger == 0)
tp = tcp_drop(tp, 0);
else {
soisdisconnecting(so);
sbflush(&so->so_rcv);
tp = tcp_usrclosed(tp);
if (tp)
tcp_output(tp);
}
return tp;
}
static struct tcpcb * tcp_newtcpcb(struct inpcb *inp)
{
struct tcpcb *tp;
tp = (struct tcpcb*)pool_get(tcppool);
if (!tp)
return NULL;
memset(tp, 0, sizeof(*tp));
tp->seg_next = tp->seg_prev = (struct tcpiphdr*)tp;
tp->t_maxseg = tcp_mssdflt;
tp->t_flags = tcp_do_rfc1323 ? (TF_REQ_SCALE | TF_REQ_TSTMP) : 0;
tp->t_inpcb = inp;
tp->t_srtt = TCPTV_SRTTBASE;
tp->t_rttvar = tcp_rttdflt * PR_SLOWHZ << 2;
tp->t_rttmin = TCPTV_MIN;
TCPT_RANGESET(tp->t_rxtcur, ((TCPTV_SRTTBASE >> 2) + (TCPTV_SRTTDFLT << 2)) >> 1,
TCPTV_MIN, TCPTV_REXMTMAX);
tp->snd_cwnd = tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;
inp->inp_ip.ip_ttl = 64;/* XXX - ip_defttl; */
inp->inp_ppcb = (caddr_t)tp;
return tp;
}
static int tcp_attach(struct socket *so)
{
struct inpcb *inp;
struct tcpcb *tp;
int error = 0;
if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
error = soreserve(so, tcp_sendspace, tcp_recvspace);
if (error)
return error;
}
error = in_pcballoc(so, &tcb);
if (error)
return error;
inp = sotoinpcb(so);
tp = tcp_newtcpcb(inp);
if (tp == NULL) {
/* we don't want to free the socket just yet, so
* record the setting of SS_NOFDREF, then clear the bit,
* detach and then reset the bit.
*/
int nofd = so->so_state & SS_NOFDREF;
so->so_state &= ~SS_NOFDREF;
in_pcbdetach(inp);
so->so_state |= nofd;
return ENOBUFS;
}
tp->t_state = TCPS_CLOSED;
return 0;
}
int tcp_userreq(struct socket *so, int req, struct mbuf *m,
struct mbuf *nam, struct mbuf *control)
{
struct inpcb *inp;
struct tcpcb *tp = NULL;
int error = 0;
int ostate;
if (req == PRU_CONTROL)
return in_control(so, (int)m, (caddr_t)nam, (struct ifnet *)control);
if (control && control->m_len) {
m_freem(control);
if (m)
m_freem(m);
return EINVAL;
}
inp = sotoinpcb(so);
/* When we're attached, the inpcb points at the socket */
if (inp == NULL && req != PRU_ATTACH)
return EINVAL;
if (inp) {
tp = intotcpcb(inp);
ostate = tp->t_state;
} else
ostate = 0;
switch(req) {
case PRU_ATTACH:
if (inp) {
error = EISCONN;
break;
}
error = tcp_attach(so);
if (error)
break;
if ((so->so_options & SO_LINGER) && so->so_linger == 0)
so->so_linger = TCP_LINGERTIME;
tp = sototcpcb(so);
break;
case PRU_DETACH:
if (tp->t_state > TCPS_LISTEN)
tp = tcp_disconnect(tp);
else
tp = tcp_close(tp);
break;
case PRU_BIND:
error = in_pcbbind(inp, nam);
break;
case PRU_LISTEN:
if (inp->lport == 0)
error = in_pcbbind(inp, NULL);
if (error == 0)
tp->t_state = TCPS_LISTEN;
break;
case PRU_CONNECT:
if (inp->lport == 0) {
error = in_pcbbind(inp, NULL);
if (error)
break;
}
error = in_pcbconnect(inp, nam);
if (error) {
printf("in_pcbconnect gave error %d\n", error);
break;
}
tp->t_template = tcp_template(tp);
if (tp->t_template == NULL) {
in_pcbdisconnect(inp);
error = ENOBUFS;
break;
}
while (tp->request_r_scale < TCP_MAX_WINSHIFT &&
(TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat)
tp->request_r_scale++;
soisconnecting(so);
tcpstat.tcps_connattempt++;
tp->t_state = TCPS_SYN_SENT;
tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
tp->iss = tcp_iss;
tcp_iss += TCP_ISSINCR / 2;
tcp_sendseqinit(tp);
error = tcp_output(tp);
break;
case PRU_CONNECT2:
error = EOPNOTSUPP;
break;
case PRU_DISCONNECT:
tp = tcp_disconnect(tp);
break;
case PRU_ACCEPT:
in_setpeeraddr(inp, nam);
break;
case PRU_SLOWTIMO:
tp = tcp_timers(tp, (int)nam);
req |= (int)nam << 8;
break;
case PRU_SEND:
sbappend(&so->so_snd, m);
error = tcp_output(tp);
break;
case PRU_RCVD:
(void) tcp_output(tp);
break;
case PRU_SHUTDOWN:
socantsendmore(so);
tp = tcp_usrclosed(tp);
if (tp)
error = tcp_output(tp);
break;
case PRU_SOCKADDR:
in_setsockaddr(inp, nam);
break;
case PRU_PEERADDR:
in_setpeeraddr(inp, nam);
break;
}
/* XXX - add tcp_trace!
if (tp && (so->so_options & SO_DEBUG))
*/
return error;
}
static struct protosw my_proto = {
"TCP Module",
TCP_MODULE_PATH,
SOCK_STREAM,
NULL,
IPPROTO_TCP,
PR_CONNREQUIRED | PR_WANTRCVD,
NET_LAYER3,
&tcp_init,
&tcp_input, /* pr_input */
NULL, /* pr_output */
&tcp_userreq, /* pr_userreq */
NULL, /* pr_sysctl */
NULL, /* pr_ctlinput */
NULL, /* pr_ctloutput */
NULL,
NULL
};
static int tcp_module_init(void *cpp)
{
if (cpp)
core = cpp;
add_domain(NULL, AF_INET);
add_protocol(&my_proto, AF_INET);
#ifndef _KERNEL_
if (!ipm) {
char path[PATH_MAX];
getcwd(path, PATH_MAX);
strcat(path, "/" IPV4_MODULE_PATH);
ipid = load_add_on(path);
if (ipid > 0) {
status_t rv = get_image_symbol(ipid, "protocol_info",
B_SYMBOL_TYPE_DATA, (void**)&ipm);
if (rv < 0) {
printf("Failed to get access to IPv4 information!\n");
return -1;
}
} else {
printf("Failed to load the IPv4 module...\n");
return -1;
}
ipm->set_core(cpp);
}
#else
if (!ipm)
get_module(IPV4_MODULE_PATH, (module_info**)&ipm);
#endif
return 0;
}
static int tcp_module_stop(void)
{
net_remove_timer(slowtim);
net_remove_timer(fasttim);
#ifndef _KERNEL_
unload_add_on(ipid);
#else
put_module(IPV4_MODULE_PATH);
#endif
remove_protocol(&my_proto);
remove_domain(AF_INET);
return 0;
}
_EXPORT struct kernel_net_module_info protocol_info = {
{
TCP_MODULE_PATH,
0,
tcp_ops
},
tcp_module_init,
tcp_module_stop
};
#ifdef _KERNEL_
static status_t tcp_ops(int32 op, ...)
{
switch(op) {
case B_MODULE_INIT:
get_module(CORE_MODULE_PATH, (module_info**)&core);
if (!core)
return B_ERROR;
return B_OK;
case B_MODULE_UNINIT:
break;
default:
return B_ERROR;
}
return B_OK;
}
_EXPORT module_info *modules[] = {
(module_info*)&protocol_info,
NULL
};
#endif
@@ -0,0 +1,209 @@
/*
* Copyright (c) 1982, 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. 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.
*
* @(#)COPYRIGHT 1.1 (NRL) 17 January 1995
*
* NRL grants permission for redistribution and use in source and binary
* forms, with or without modification, of the software and documentation
* created at NRL 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgements:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* This product includes software developed at the Information
* Technology Division, US Naval Research Laboratory.
* 4. Neither the name of the NRL nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL 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 NRL 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.
*
* The views and conclusions contained in the software and documentation
* are those of the authors and should not be interpreted as representing
* official policies, either expressed or implied, of the US Naval
* Research Laboratory (NRL).
*/
#ifdef TCPDEBUG
/* load symbolic names */
#define PRUREQUESTS
#define TCPSTATES
#define TCPTIMERS
#define TANAMES
#endif
#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/socket.h>
#include <net/route.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <netinet/in_pcb.h>
#include <netinet/ip_var.h>
#include <netinet/tcp.h>
#include <netinet/tcp_timer.h>
#include <netinet/tcp_var.h>
#include <netinet/tcpip.h>
#include <netinet/tcp_debug.h>
#ifdef INET6
#ifndef INET
#include <netinet/in.h>
#endif
#include <netinet/ip6.h>
#endif /* INET6 */
#ifdef TCPDEBUG
int tcpconsdebug = 0;
#endif
/*
* Tcp debug routines
*/
void tcp_trace(int16 act, int16 ostate, struct tcpcb *tp, void *headers,
int req, int len)
{
#ifdef TCPDEBUG
tcp_seq seq, ack;
int flags;
#endif
struct tcp_debug *td = &tcp_debug[tcp_debx++];
struct tcpiphdr *ti = (struct tcpiphdr *)headers;
struct tcphdr *th;
#ifdef INET6
struct tcpipv6hdr *ti6 = (struct tcpipv6hdr *)ti;
#endif
if (tcp_debx == TCP_NDEBUG)
tcp_debx = 0;
td->td_time = iptime();
td->td_act = act;
td->td_ostate = ostate;
td->td_tcb = (caddr_t)tp;
if (tp)
td->td_cb = *tp;
else
memset((caddr_t)&td->td_cb, 0, sizeof (*tp));
#ifdef INET6
if (tp->pf == PF_INET6) {
if (ti) {
th = &ti6->ti6_t;
td->td_ti6 = *ti6;
} else {
memset(&td->td_ti6, 0, sizeof(struct tcpipv6hdr));
}
} else
#endif /* INET6 */
{
if (ti) {
th = &ti->ti_t;
td->td_ti = *ti;
} else {
memset(&td->td_ti, 0, sizeof(struct tcpiphdr));
}
}
td->td_req = req;
#ifdef TCPDEBUG
if (tcpconsdebug == 0)
return;
if (tp)
printf("%x %s:", tp, tcpstates[ostate]);
else
printf("???????? ");
printf("%s ", tanames[act]);
switch (act) {
case TA_INPUT:
case TA_OUTPUT:
case TA_DROP:
if (ti == 0)
break;
seq = th->th_seq;
ack = th->th_ack;
if (act == TA_OUTPUT) {
seq = ntohl(seq);
ack = ntohl(ack);
}
if (len)
printf("[%x..%x)", seq, seq+len);
else
printf("%x", seq);
printf("@%x, urp=%x", ack, th->th_urp);
flags = th->th_flags;
if (flags) {
#ifndef lint
char *cp = "<";
#define pf(f) { if (th->th_flags&TH_##f) { printf("%s%s", cp, "f"); cp = ","; } }
pf(SYN); pf(ACK); pf(FIN); pf(RST); pf(PUSH); pf(URG);
#endif
printf(">");
}
break;
case TA_USER:
printf("%s", prurequests[req&0xff]);
if ((req & 0xff) == PRU_SLOWTIMO)
printf("<%s>", tcptimers[req>>8]);
break;
}
if (tp)
printf(" -> %s", tcpstates[tp->t_state]);
/* print out internal state of tp !?! */
printf("\n");
if (tp == 0)
return;
printf("\trcv_(nxt,wnd,up) (%x,%x,%x) snd_(una,nxt,max) (%x,%x,%x)\n",
tp->rcv_nxt, tp->rcv_wnd, tp->rcv_up, tp->snd_una, tp->snd_nxt,
tp->snd_max);
printf("\tsnd_(wl1,wl2,wnd) (%x,%x,%x)\n",
tp->snd_wl1, tp->snd_wl2, tp->snd_wnd);
#endif /* TCPDEBUG */
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,454 @@
/* tcp_output.c */
#ifndef _KERNEL_
#include <stdio.h>
#endif
#include "sys/socketvar.h"
#include "sys/protosw.h"
#include "netinet/in.h"
#include "netinet/in_pcb.h"
#include "netinet/ip_var.h"
#include "netinet/tcp.h"
#include "netinet/tcp_timer.h"
#include "netinet/tcp_var.h"
#include "netinet/tcpip.h"
#include "netinet/tcp_seq.h"
#define TCPOUTFLAGS
#include "netinet/tcp_fsm.h"
#include "netinet/tcp_debug.h"
#include "core_module.h"
#include "core_funcs.h"
#include "ipv4/ipv4_module.h"
#ifdef _KERNEL_
#include <KernelExport.h>
#endif
extern struct core_module_info *core;
extern struct ipv4_module_info *ipm;
extern struct pool_ctl *tcppool;
#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
extern uint32 sb_max; /* defined in socketvar.h */
void tcp_setpersist(struct tcpcb *tp)
{
int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1;
if (tp->t_timer[TCPT_REXMT]) {
printf("PANIC: tcp_output REXMT\n");
return;
}
/* Start/reset the persistance timer */
TCPT_RANGESET(tp->t_timer[TCPT_PERSIST],
t * tcp_backoff[tp->t_rxtshift],
TCPTV_PERSMIN, TCPTV_PERSMAX);
if (tp->t_rxtshift < TCP_MAXRXTSHIFT)
tp->t_rxtshift++;
}
int tcp_mss(struct tcpcb *tp, uint offer)
{
struct route *ro;
struct rtentry *rt;
struct ifnet *ifp;
int rtt, mss;
uint32 bufsize;
struct inpcb *inp;
struct socket *so;
inp = tp->t_inpcb;
ro = &inp->inp_route;
if ((rt = ro->ro_rt) == NULL) {
/* don't have a route, get one if we can */
if (inp->faddr.s_addr != INADDR_ANY) {
memset(&ro->ro_dst, 0, sizeof(ro->ro_dst));
ro->ro_dst.sa_family = AF_INET;
ro->ro_dst.sa_len = sizeof(ro->ro_dst);
((struct sockaddr_in *)&ro->ro_dst)->sin_addr = inp->faddr;
rtalloc(ro);
}
if ((rt = ro->ro_rt) == NULL)
return tcp_mssdflt;
}
ifp=rt->rt_ifp;
so = inp->inp_socket;
if (tp->t_srtt == 0 && (rtt = rt->rt_rmx.rmx_rtt)) {
if (rt->rt_rmx.rmx_locks & RTV_RTT)
tp->t_rttmin = rtt / (RTM_RTTUNIT / PR_SLOWHZ);
tp->t_srtt = rtt / (RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTT_SCALE));
if (rt->rt_rmx.rmx_rttvar)
tp->t_rttvar = rt->rt_rmx.rmx_rttvar /
(RTM_RTTUNIT / (PR_SLOWHZ * TCP_RTTVAR_SCALE));
else
tp->t_rttvar = tp->t_srtt * TCP_RTTVAR_SCALE / TCP_RTT_SCALE;
TCPT_RANGESET(tp->t_rxtcur,
((tp->t_srtt >> 2) + tp->t_rttvar) >> 1,
tp->t_rttmin, TCPTV_REXMTMAX);
}
if (rt->rt_rmx.rmx_mtu)
mss = rt->rt_rmx.rmx_mtu - sizeof(struct tcpiphdr);
else {
mss = ifp->if_mtu - sizeof(struct tcpiphdr);
#if (MCLBYTES & (MCLBYTES - 1)) == 0
if (mss > MCLBYTES)
mss &=~ (MCLBYTES - 1);
else
if (mss > MCLBYTES)
mss = mss / MCLBYTES * MCLBYTES;
#endif
if (!in_localaddr(inp->faddr))
mss = min(mss, tcp_mssdflt);
}
if (offer)
mss = min(mss, offer);
mss = max(mss, 32);
if (mss < tp->t_maxseg || offer != 0) {
if ((bufsize = rt->rt_rmx.rmx_sendpipe) == 0)
bufsize = so->so_snd.sb_hiwat;
if (bufsize < mss)
mss = bufsize;
else {
bufsize = roundup(bufsize, mss);
if (bufsize > sb_max)
bufsize = sb_max;
sbreserve(&so->so_snd, bufsize);
}
tp->t_maxseg = mss;
if ((bufsize = rt->rt_rmx.rmx_recvpipe) == 0)
bufsize = so->so_rcv.sb_hiwat;
if (bufsize > mss) {
bufsize = roundup(bufsize, mss);
if (bufsize > sb_max)
bufsize = sb_max;
sbreserve(&so->so_rcv, bufsize);
}
}
tp->snd_cwnd = mss;
if (rt->rt_rmx.rmx_ssthresh) {
tp->snd_ssthresh = max(2 * mss, rt->rt_rmx.rmx_ssthresh);
}
return mss;
}
void tcp_quench(struct inpcb *inp, int error)
{
struct tcpcb *tp = intotcpcb(inp);
if(tp)
tp->snd_cwnd = tp->t_maxseg;
}
int tcp_output(struct tcpcb *tp)
{
struct socket *so = tp->t_inpcb->inp_socket;
int32 len, win;
int off, flags, error = 0;
struct mbuf *m;
struct tcpiphdr *ti;
u_char opt[MAX_TCPOPTLEN];
uint optlen, hdrlen;
int idle, sendalot;
idle = (tp->snd_max == tp->snd_una);
if (idle && tp->t_idle >= tp->t_rxtcur)
/* Basically have we been idle for a while?
* If so, slow start to get ack "clock" running again
*/
tp->snd_cwnd = tp->t_maxseg;
again:
sendalot = 0;
off = tp->snd_nxt - tp->snd_una;
win = min(tp->snd_wnd, tp->snd_cwnd);
flags = tcp_outflags[tp->t_state];
/* if we're in a persist window of 0, send 1 byte
* Otherwise, if we have a small but nonzero window and
* the timer has expired, send what we can and go to
* transmit state
*/
if (tp->t_force) {
if (win == 0) {
/* If we have data to send, clear the FIN bit.
*/
if (off < so->so_snd.sb_cc)
flags &= ~TH_FIN;
win = 1;
} else {
tp->t_timer[TCPT_PERSIST] = 0;
tp->t_rxtshift = 0;
}
}
len = min(so->so_snd.sb_cc, win) - off;
if (len < 0) {
/* if FIN has been sent but not ack'd,
* but we haven't been asked to retransmit, len would be -1
* Otherwise window shrank after we sent into it. If window
* shrank to 0, cancel pending transmit and pull snd_nxt
* back to (closed) window.
*/
len = 0;
if (win == 0) {
tp->t_timer[TCPT_REXMT] = 0;
tp->snd_nxt = tp->snd_una;
}
}
if (len > tp->t_maxseg) {
len = tp->t_maxseg;
sendalot = 1;
}
if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + so->so_snd.sb_cc))
flags &= ~TH_FIN;
win = sbspace(&so->so_rcv);
/* Do we have a reason to send anything? */
if (len) {
if (len == tp->t_maxseg)
goto send;
if ((idle || tp->t_flags & TF_NODELAY) &&
len + off >= so->so_snd.sb_cc)
goto send;
if (tp->t_force)
goto send;
if (len >= tp->max_sndwnd / 2)
goto send;
if (SEQ_LT(tp->snd_nxt, tp->snd_max))
goto send;
}
if (win > 0) {
int32 adv = min(win, (int32)TCP_MAXWIN << tp->rcv_scale) -
(tp->rcv_adv - tp->rcv_nxt);
if (adv >= (int32)(2 * tp->t_maxseg))
goto send;
if (2 * adv >= (int32)so->so_rcv.sb_hiwat)
goto send;
}
if (tp->t_flags & TF_ACKNOW)
goto send;
if (flags & (TH_SYN | TH_RST))
goto send;
if (SEQ_GT(tp->snd_up, tp->snd_una))
goto send;
if ((flags & TH_FIN) && ((tp->t_flags & TF_SENTFIN) == 0 ||
tp->snd_nxt == tp->snd_una))
goto send;
if (so->so_snd.sb_cc && tp->t_timer[TCPT_REXMT] == 0 &&
(tp->t_timer[TCPT_PERSIST] == 0)) {
tp->t_rxtshift = 0;
tcp_setpersist(tp);
}
/* We don't have a reason to send anything for this connection,
* so just return.
*/
return 0;
send:
optlen = 0;
hdrlen = sizeof(struct tcpiphdr);
if (flags & TH_SYN) {
tp->snd_nxt = tp->iss;
if ((tp->t_flags & TF_NOOPT) == 0) {
uint16 mss;
opt[0] = TCPOPT_MAXSEG;
opt[1] = 4;
mss = htons((uint16)tcp_mss(tp, 0));
memcpy((caddr_t)opt + 2, &mss, sizeof(mss));
optlen = 4;
if ((tp->t_flags & TF_REQ_SCALE) &&
((flags & TH_ACK) == 0 ||
(tp->t_flags & TF_RCVD_SCALE))) {
*((uint32*)(opt + optlen)) = htonl (TCPOPT_NOP << 24 |
TCPOPT_WINDOW << 16 |
TCPOLEN_WINDOW << 8 |
tp->request_r_scale);
optlen += 4;
}
}
}
if ((tp->t_flags & (TF_REQ_TSTMP | TF_NOOPT)) == TF_REQ_TSTMP &&
(flags & TH_RST) == 0 &&
((flags & (TH_SYN | TH_ACK)) == TH_SYN ||
(tp->t_flags & TF_RCVD_TSTMP))) {
uint32 *lp = (uint32*)(opt + optlen);
*lp++ = htonl(TCPOPT_TSTAMP_HDR);
*lp++ = htonl(tcp_now);
*lp = htonl(tp->ts_recent);
optlen += TCPOLEN_TSTAMP_APPA;
}
hdrlen += optlen;
if (len > tp->t_maxseg - optlen) {
len = tp->t_maxseg - optlen;
sendalot = 1;
}
if (len) {
if (tp->t_force && len == 1)
tcpstat.tcps_sndprobe++;
else if (SEQ_LT(tp->snd_nxt, tp->snd_max)) {
tcpstat.tcps_sndrexmitpack++;
tcpstat.tcps_sndrexmitbyte += len;
} else {
tcpstat.tcps_sndpack++;
tcpstat.tcps_sndbyte += len;
}
m = m_gethdr(MT_HEADER);
if (m == NULL) {
printf("tcp_output: ENOBUFS\n");
error = ENOBUFS;
goto out;
}
m->m_data += max_linkhdr;
m->m_len = hdrlen;
if (len <= MHLEN - hdrlen - max_linkhdr) {
m_copydata(so->so_snd.sb_mb, off, (int)len, mtod(m, caddr_t) + hdrlen);
m->m_len += len;
} else {
m->m_next = m_copym(so->so_snd.sb_mb, off, (int)len);
if (m->m_next == NULL)
len = 0;
}
if (off + len == so->so_snd.sb_cc)
flags |= TH_PUSH;
} else {
if (tp->t_flags & TF_ACKNOW)
tcpstat.tcps_sndacks++;
else if (flags & (TH_SYN | TH_FIN | TH_RST))
tcpstat.tcps_sndctrl++;
else if (SEQ_GT(tp->snd_up, tp->snd_una))
tcpstat.tcps_sndurg++;
else
tcpstat.tcps_sndwinup++;
m = m_gethdr(MT_HEADER);
if (m == NULL) {
printf("tcp_output: ENOBUFS\n");
error = ENOBUFS;
goto out;
}
m->m_data += max_linkhdr;
m->m_len = hdrlen;
}
m->m_pkthdr.rcvif = NULL;
ti = mtod(m, struct tcpiphdr*);
if (tp->t_template == NULL)
printf("tcp_output: PANIC t_template == NULL\n");
memcpy((caddr_t)ti, (caddr_t)tp->t_template, sizeof(struct tcpiphdr));
if (flags & TH_FIN && (tp->t_flags & TF_SENTFIN) &&
(tp->snd_nxt == tp->snd_max))
tp->snd_nxt--;
if (len || (flags & (TH_SYN | TH_FIN)) || tp->t_timer[TCPT_PERSIST])
ti->ti_seq = htonl(tp->snd_nxt);
else
ti->ti_seq = htonl(tp->snd_max);
ti->ti_ack = htonl(tp->rcv_nxt);
if (optlen) {
memcpy((caddr_t)(ti + 1), (caddr_t)opt, optlen);
ti->ti_off = (sizeof(struct tcphdr) + optlen) >> 2;
}
ti->ti_flags = flags;
if (win < (int32)(so->so_rcv.sb_hiwat / 4) &&
win < (int32) tp->t_maxseg)
win = 0;
if (win > (int32) TCP_MAXWIN << tp->rcv_scale)
win = (int32) TCP_MAXWIN << tp->rcv_scale;
if (win < (int32)(tp->rcv_adv - tp->rcv_nxt))
win = (int32)(tp->rcv_adv - tp->rcv_nxt);
ti->ti_win = htons((uint16)(win >> tp->rcv_scale));
if (SEQ_GT(tp->snd_up, tp->snd_nxt)) {
ti->ti_urp = htons((uint16)(tp->snd_up - tp->snd_nxt));
ti->ti_flags |= TH_URG;
} else
tp->snd_up = tp->snd_una;
if (len + optlen)
ti->ti_len = htons((uint16)(sizeof(struct tcphdr) + optlen + len));
ti->ti_sum = in_cksum(m, (int)(hdrlen+len), 0);
if (tp->t_force == 0 || tp->t_timer[TCPT_PERSIST] == 0) {
tcp_seq startseq = tp->snd_nxt;
if (flags & (TH_SYN | TH_FIN)) {
tp->snd_nxt++;
if (flags & TH_FIN)
tp->t_flags |= TF_SENTFIN;
}
tp->snd_nxt += len;
if (SEQ_GT(tp->snd_nxt, tp->snd_max)) {
tp->snd_max = tp->snd_nxt;
if (tp->t_rtt == 0) {
tp->t_rtt = 1;
tp->t_rtseq = startseq;
tcpstat.tcps_segstimed++;
}
}
if (tp->t_timer[TCPT_REXMT] == 0 &&
tp->snd_nxt != tp->snd_una) {
tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
if (tp->t_timer[TCPT_PERSIST]) {
tp->t_timer[TCPT_PERSIST] = 0;
tp->t_rxtshift = 0;
}
}
} else if (SEQ_GT(tp->snd_nxt + len, tp->snd_max))
tp->snd_max = tp->snd_nxt + len;
if (so->so_options & SO_DEBUG)
tcp_trace(TA_OUTPUT, tp->t_state, tp, ti, 0, len);
m->m_pkthdr.len = hdrlen + len;
((struct ip*)ti)->ip_len = m->m_pkthdr.len;
((struct ip*)ti)->ip_ttl = tp->t_inpcb->inp_ip.ip_ttl;
((struct ip*)ti)->ip_tos = tp->t_inpcb->inp_ip.ip_tos;
error = ipm->output(m, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
so->so_options & SO_DONTROUTE, NULL);
if (error) {
out:
if (error == ENOBUFS) {
tcp_quench(tp->t_inpcb, 0);
return 0;
}
if ((error == EHOSTUNREACH || error == ENETDOWN) &&
TCPS_HAVERCVDSYN(tp->t_state)) {
tp->t_softerror = error;
return 0;
}
return error;
}
tcpstat.tcps_sndtotal++;
if (win > 0 && SEQ_GT(tp->rcv_nxt + win, tp->rcv_adv))
tp->rcv_adv = tp->rcv_nxt + win;
tp->last_ack_sent = tp->rcv_nxt;
tp->t_flags &= ~(TF_ACKNOW | TF_DELACK);
if (sendalot)
goto again;
return 0;
}
@@ -0,0 +1,182 @@
/* tcp_timer.c */
#ifndef _KERNEL_
#include <stdio.h>
#endif
#include "sys/protosw.h"
#include "netinet/in_pcb.h"
#include "netinet/tcp.h"
#include "netinet/tcp_timer.h"
#include "netinet/tcp_var.h"
#include "netinet/tcp_seq.h"
#include "netinet/tcp_fsm.h"
#include "core_module.h"
#include "core_funcs.h"
#ifdef _KERNEL_
#include <KernelExport.h>
#endif
extern struct core_module_info *core;
int tcp_backoff[TCP_MAXRXTSHIFT + 1] =
{ 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64 };
int tcp_totbackoff = 511; /* sum of tcp_backoff[] */
int tcp_keepidle = TCPTV_KEEP_IDLE;
int tcp_keepintvl = TCPTV_KEEPINTVL;
int tcp_maxpersistidle = TCPTV_KEEP_IDLE; /* max idle time in persist */
int tcp_maxidle;
void tcp_canceltimers(struct tcpcb *tp)
{
int i;
for (i=0; i < TCPT_NTIMERS; i++)
tp->t_timer[i] = 0;
}
struct tcpcb * tcp_timers(struct tcpcb *tp, int timer)
{
int rexmt;
switch (timer) {
/* TCPT_2MSL is used for
* FIN_WAIT2 timer
* TIME_WAIT
*/
case TCPT_2MSL:
if (tp->t_state != TCPS_TIME_WAIT &&
tp->t_idle <= tcp_maxidle)
tp->t_timer[TCPT_2MSL] = tcp_keepintvl;
else
tp = tcp_close(tp);
break;
/* TCPT_PERSIST is used to wait for being told it can send
* data. The window ahs been set to 0, so no data can be
* sent, but there's data waiting to be sent. when the timer
* expires we'll force a byte to be sent (despite the window
* being 0) and reset the time...
*/
case TCPT_PERSIST:
tcpstat.tcps_persisttimeo++;
tcp_setpersist(tp);
tp->t_force = 1;
tcp_output(tp);
tp->t_force = 0;
break;
/* TCPT_KEEP is used for
* send data
* drop connection if too long idle
*/
case TCPT_KEEP:
tcpstat.tcps_keeptimeo++;
if (tp->t_state < TCPS_ESTABLISHED)
goto dropit;
if (tp->t_inpcb->inp_socket->so_options & SO_KEEPALIVE &&
tp->t_state <= TCPS_CLOSE_WAIT) {
if (tp->t_idle >= tcp_keepidle + tcp_maxidle)
goto dropit;
tcpstat.tcps_keepprobe++;
tcp_respond(tp, tp->t_template, NULL, tp->rcv_nxt,
tp->snd_una - 1, 0);
tp->t_timer[TCPT_KEEP] = tcp_keepintvl;
} else
tp->t_timer[TCPT_KEEP] = tcp_keepidle;
break;
dropit:
tcpstat.tcps_keepdrops++;
tp = tcp_drop(tp, ETIMEDOUT);
break;
/* TCPT_REXMT is the transmission timer */
case TCPT_REXMT:
if (++tp->t_rxtshift > TCP_MAXRXTSHIFT) {
tp->t_rxtshift = TCP_MAXRXTSHIFT;
tcpstat.tcps_timeoutdrop++;
tp = tcp_drop(tp, tp->t_softerror ? tp->t_softerror : ETIMEDOUT);
break;
}
tcpstat.tcps_rexmttimeo++;
rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift];
TCPT_RANGESET(tp->t_rxtcur, rexmt, tp->t_rttmin, TCPTV_REXMTMAX);
tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4) {
in_losing(tp->t_inpcb);
tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT);
tp->t_srtt = 0;
}
tp->snd_nxt = tp->snd_una;
tp->t_rtt = 0;
{
uint win = min(tp->snd_wnd, tp->snd_cwnd) / 2/ tp->t_maxseg;
if (win < 2)
win = 2;
tp->snd_cwnd = tp->t_maxseg;
tp->snd_ssthresh = win * tp->t_maxseg;
tp->t_dupacks = 0;
}
tcp_output(tp);
break;
}
return (tp);
}
void tcp_slowtimer(void *data)
{
struct inpcb *ip, *ipnxt;
struct tcpcb *tp;
int i;
tcp_maxidle = TCPTV_KEEPCNT * tcp_keepintvl;
ip = tcb.inp_next;
if (!ip)
return;
for (; ip != &tcb; ip = ipnxt) {
ipnxt = ip->inp_next;
tp = intotcpcb(ip);
if (!tp)
continue;
for (i=0;i < TCPT_NTIMERS;i++) {
if (tp->t_timer[i] && --tp->t_timer[i] == 0) {
tcp_userreq(tp->t_inpcb->inp_socket, PRU_SLOWTIMO, NULL,
(struct mbuf *)i, NULL);
if (ipnxt->inp_prev != ip)
goto tpgone;
}
}
tp->t_idle++;
if (tp->t_rtt)
tp->t_rtt++;
tpgone:
; /* mwcc wants a ; here, so it gets one */
}
tcp_iss += TCP_ISSINCR / PR_SLOWHZ;
tcp_now++;
return;
}
void tcp_fasttimer(void *data)
{
struct inpcb *inp;
struct tcpcb *tp;
inp = tcb.inp_next;
if (inp) {
for (; inp != &tcb; inp = inp->inp_next) {
if ((tp = (struct tcpcb*)inp->inp_ppcb) &&
(tp->t_flags & TF_DELACK)) {
tp->t_flags &= ~TF_DELACK;
tp->t_flags |= TF_ACKNOW;
tcpstat.tcps_delack++;
tcp_output(tp);
}
}
}
return;
}
@@ -0,0 +1,449 @@
/* udp.c
*/
#ifndef _KERNEL_
#include <stdio.h>
#include <string.h>
#endif
#include "net_misc.h"
#include "protocols.h"
#include "netinet/in_systm.h"
#include "netinet/in_var.h"
#include "netinet/in_pcb.h"
#include "netinet/ip.h"
#include "sys/domain.h"
#include "sys/protosw.h"
#include "netinet/ip_var.h"
#include "netinet/udp.h"
#include "netinet/udp_var.h"
#include "netinet/ip_icmp.h"
#include "core_module.h"
#include "net_module.h"
#include "core_funcs.h"
#include "../icmp/icmp_module.h"
#ifdef _KERNEL_
#include <KernelExport.h>
static status_t udp_ops(int32 op, ...);
#define UDP_MODULE_PATH "network/protocol/udp"
#else /* _KERNEL */
#define udp_ops NULL
#define UDP_MODULE_PATH "modules/protocol/udp"
#endif
/* Private variables */
static struct core_module_info *core = NULL;
static struct protosw *proto[IPPROTO_MAX];
static struct inpcb udb; /* head of the UDP PCB list! */
static int udpcksum = 1; /* do we calculate the UDP checksum? */
static struct udpstat udpstat;
static uint32 udp_sendspace; /* size of send buffer */
static uint32 udp_recvspace; /* size of recieve buffer */
static struct icmp_module_info *icmp = NULL;
#ifndef _KERNEL_
static image_id icmpid = -1;
#endif
static struct in_addr zeroin_addr = {0};
/* Private but used globally, need to make thread safe... tls??? */
static struct inpcb *udp_last_inpcb = NULL;
static struct sockaddr_in udp_in;
#if SHOW_DEBUG
static void dump_udp(struct mbuf *buf)
{
struct ip *ip = mtod(buf, struct ip*);
struct udphdr *udp = (struct udphdr*)((caddr_t)ip + (ip->ip_hl * 4));
printf("udp_header :\n");
printf(" : src_port : %d\n", ntohs(udp->src_port));
printf(" : dst_port : %d\n", ntohs(udp->dst_port));
printf(" : udp length : %d bytes\n", ntohs(udp->length));
}
#endif /* SHOW_DEBUG */
int udp_output(struct inpcb *inp, struct mbuf *m, struct mbuf *addr,struct mbuf *control)
{
struct udpiphdr *ui;
uint16 len = m->m_pkthdr.len;
uint16 hdrlen = len + (uint16)sizeof(struct udpiphdr);
struct in_addr laddr;
int error = 0;
if (control)
m_freem(control);
if (addr) {
laddr = inp->laddr;
if (inp->faddr.s_addr != INADDR_ANY) {
error = EISCONN;
goto release;
}
error = in_pcbconnect(inp, addr);
if (error)
goto release;
} else {
if (inp->faddr.s_addr == INADDR_ANY) {
error = ENOTCONN;
goto release;
}
}
M_PREPEND(m, sizeof(*ui));
if (!m) {
error = ENOMEM;
goto release;
}
ui = mtod(m, struct udpiphdr *);
ui->ui_next = ui->ui_prev = NULL;
ui->ui_x1 = 0;
ui->ui_pr = IPPROTO_UDP;
ui->ui_len = htons(len + sizeof(struct udphdr));
ui->ui_src = inp->laddr;
ui->ui_dst = inp->faddr;
ui->ui_sport = inp->lport;
ui->ui_dport = inp->fport;
ui->ui_ulen = ui->ui_len;
ui->ui_sum = 0;
if (udpcksum)
ui->ui_sum = in_cksum(m, hdrlen, 0);
if (ui->ui_sum == 0)
ui->ui_sum = 0xffff;
((struct ip*)ui)->ip_len = hdrlen;
((struct ip*)ui)->ip_ttl = 64; /* XXX - Fix this! */
((struct ip*)ui)->ip_tos = 0; /* XXX - Fix this! */
/* XXX - add multicast options when available! */
error = proto[IPPROTO_IP]->pr_output(m,
inp->inp_options, &inp->inp_route,
inp->inp_socket->so_options & (SO_DONTROUTE | SO_BROADCAST),
NULL /* inp->inp_moptions */ );
if (addr) {
in_pcbdisconnect(inp); /* remove temporary route */
inp->laddr = laddr;
}
return error;
release:
m_freem(m);
return error;
}
int udp_userreq(struct socket *so, int req,
struct mbuf *m, struct mbuf *addr, struct mbuf *ctrl)
{
struct inpcb *inp = sotoinpcb(so);
int error = 0;
if (req == PRU_CONTROL)
return in_control(so, (int)m, (caddr_t)addr, (struct ifnet *)ctrl);
if (inp == NULL && req != PRU_ATTACH) {
error = EINVAL;
goto release;
}
switch (req) {
case PRU_ATTACH:
/* we don't replace an existing inpcb! */
if (inp != NULL) {
error = EINVAL;
break;
}
error = in_pcballoc(so, &udb); /* udp head */
if (error)
break;
error = soreserve(so, udp_sendspace, udp_recvspace);
if (error)
break;
/* XXX - this is a hack! This should be the default ip TTL */
((struct inpcb*) so->so_pcb)->inp_ip.ip_ttl = 64;
break;
case PRU_DETACH:
/* This should really be protected when in kernel... */
if (inp == udp_last_inpcb)
udp_last_inpcb = &udb;
in_pcbdetach(inp);
break;
case PRU_BIND:
/* XXX - locking */
error = in_pcbbind(inp, addr);
break;
case PRU_SEND:
/* we can use this as we're in the same module... */
return udp_output(inp, m, addr, ctrl);
case PRU_LISTEN:
error = EINVAL;//EOPNOTSUPP;
break;
case PRU_CONNECT:
if (inp->faddr.s_addr != INADDR_ANY) {
error = EISCONN;
break;
}
error = in_pcbconnect(inp, addr);
if (error == 0)
soisconnected(so);
break;
case PRU_CONNECT2:
error = EINVAL;//EOPNOTSUPP;
break;
case PRU_ACCEPT:
error = EINVAL;//EOPNOTSUPP;
break;
case PRU_DISCONNECT:
if (inp->faddr.s_addr == INADDR_ANY) {
error = ENOTCONN;
break;
}
in_pcbdisconnect(inp);
inp->laddr.s_addr = INADDR_ANY;
so->so_state &= ~SS_ISCONNECTED;
break;
case PRU_SOCKADDR:
in_setsockaddr(inp, addr);
break;
case PRU_PEERADDR:
in_setpeeraddr(inp, addr);
break;
case PRU_SENSE:
/* will we ever see one of these???? */
/* generated by an fstat on bsd... */
return 0;
default:
printf("Unknown options passed to udp_userreq (%d)\n", req);
}
release:
if (ctrl) {
printf("UDP control retained!\n");
m_freem(ctrl);
}
if (m)
m_freem(m);
return error;
}
void udp_input(struct mbuf *buf, int hdrlen)
{
struct ip *ip = mtod(buf, struct ip*);
struct udphdr *udp = (struct udphdr*)((caddr_t)ip + hdrlen);
uint16 ck = 0;
int len;
struct ip saved_ip;
struct mbuf *opts = NULL;
struct inpcb *inp = NULL;
#if SHOW_DEBUG
dump_udp(buf);
#endif
/* check and adjust sizes as required... */
len = ntohs(udp->uh_ulen) + hdrlen;
saved_ip = *ip;
if (udpcksum && udp->uh_sum) {
((struct ipovly*)ip)->ih_next = ((struct ipovly*)ip)->ih_prev = NULL;
((struct ipovly*)ip)->ih_x1 = 0;
((struct ipovly*)ip)->ih_len = udp->uh_ulen;
/* XXX - if we have options we need to be careful when calculating the
* checksum here...
*/
if ((ck = in_cksum(buf, len, 0)) != 0) {
udpstat.udps_badsum++;
m_freem(buf);
printf("udp_input: UDP Checksum check failed. (%d over %ld bytes)\n", ck, len + sizeof(*ip));
return;
}
}
inp = udp_last_inpcb;
if (inp == NULL ||
inp->lport != udp->uh_dport ||
inp->fport != udp->uh_sport ||
inp->faddr.s_addr != ip->ip_src.s_addr ||
inp->laddr.s_addr != ip->ip_dst.s_addr) {
inp = in_pcblookup(&udb, ip->ip_src, udp->uh_sport,
ip->ip_dst, udp->uh_dport, INPLOOKUP_WILDCARD);
if (inp)
udp_last_inpcb = inp;
}
if (!inp) {
atomic_add((vint32 *)&udpstat.udps_noport, 1);
if (buf->m_flags & (M_BCAST | M_MCAST)) {
atomic_add((vint32 *)&udpstat.udps_noportbcast, 1);
goto bad;
}
*ip = saved_ip;
ip->ip_len += hdrlen;
icmp->error(buf, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0);
return;
}
udp_in.sin_port = udp->uh_sport;
udp_in.sin_addr = ip->ip_src;
if (inp->inp_flags & INP_CONTROLOPT) {
printf("INP Control Options to process!\n");
/* XXX - add code to do this... */
}
hdrlen += sizeof(struct udphdr);
buf->m_len -= hdrlen;
buf->m_pkthdr.len -= hdrlen;
buf->m_data += hdrlen;
if (sbappendaddr(&inp->inp_socket->so_rcv, (struct sockaddr*)&udp_in,
buf, opts) == 0) {
goto bad;
}
sorwakeup(inp->inp_socket);
return;
bad:
if (opts)
m_freem(opts);
m_freem(buf);
return;
}
static void udp_notify(struct inpcb *inp, int err)
{
inp->inp_socket->so_error = err;
sorwakeup(inp->inp_socket);
sowwakeup(inp->inp_socket);
}
static void udp_ctlinput(int cmd, struct sockaddr *sa, void *ipp)
{
struct ip *ip = (struct ip*)ipp;
struct udphdr *uh;
if (!PRC_IS_REDIRECT(cmd) &&
((uint)cmd >= PRC_NCMDS || inetctlerrmap(cmd) == 0))
return;
if (ip) {
uh = (struct udphdr *)((char *) ip + (ip->ip_hl << 2));
in_pcbnotify(&udb, sa, uh->uh_dport, ip->ip_src, uh->uh_sport, cmd, udp_notify);
} else
in_pcbnotify(&udb, sa, 0, zeroin_addr, 0, cmd, udp_notify);
}
void udp_init(void)
{
udb.inp_prev = udb.inp_next = &udb;
udp_sendspace = 9216; /* default size */
udp_recvspace = 41600; /* default size */
udp_in.sin_len = sizeof(udp_in);
memset(&udpstat, 0, sizeof(udpstat));
memset(proto, 0, sizeof(struct protosw *) * IPPROTO_MAX);
add_protosw(proto, NET_LAYER2);
}
static struct protosw my_proto = {
"UDP Module",
UDP_MODULE_PATH,
SOCK_DGRAM,
NULL,
IPPROTO_UDP,
PR_ATOMIC | PR_ADDR,
NET_LAYER3,
&udp_init,
&udp_input,
NULL, /* pr_output */
&udp_userreq,
NULL, /* pr_sysctl */
&udp_ctlinput,
NULL, /* pr_ctloutput */
NULL,
NULL
};
static int udp_module_init(void *cpp)
{
if (cpp)
core = cpp;
add_domain(NULL, AF_INET);
add_protocol(&my_proto, AF_INET);
#ifndef _KERNEL_
if (!icmp) {
char path[PATH_MAX];
getcwd(path, PATH_MAX);
strcat(path, "/" ICMP_MODULE_PATH);
icmpid = load_add_on(path);
if (icmpid > 0) {
status_t rv = get_image_symbol(icmpid, "protocol_info",
B_SYMBOL_TYPE_DATA, (void**)&icmp);
if (rv < 0) {
printf("Failed to get access to IPv4 information!\n");
return -1;
}
} else {
printf("Failed to load the IPv4 module...\n");
return -1;
}
icmp->set_core(cpp);
}
#else
if (!icmp)
get_module(ICMP_MODULE_PATH, (module_info**)&icmp);
#endif
return 0;
}
static int udp_module_stop(void)
{
remove_protocol(&my_proto);
remove_domain(AF_INET);
return 0;
}
_EXPORT struct kernel_net_module_info protocol_info = {
{
UDP_MODULE_PATH,
B_KEEP_LOADED,
udp_ops
},
udp_module_init,
udp_module_stop
};
#ifdef _KERNEL_
static status_t udp_ops(int32 op, ...)
{
switch(op) {
case B_MODULE_INIT:
get_module(CORE_MODULE_PATH, (module_info**)&core);
if (!core)
return B_ERROR;
return B_OK;
case B_MODULE_UNINIT:
break;
default:
return B_ERROR;
}
return B_OK;
}
_EXPORT module_info *modules[] = {
(module_info *)&protocol_info,
NULL
};
#endif