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:
@@ -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 ;
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
SubDir OBOS_TOP src add-ons kernel network core ;
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 = ⊤
|
||||
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 = 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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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;//ðer_input;
|
||||
ifp->output = ðer_output;
|
||||
ifp->stop = ðer_dev_stop;
|
||||
ifp->ioctl = ðer_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(ð->src);
|
||||
printf(" to ");
|
||||
print_ether_addr(ð->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*)ð->ether_dhost, (void*)ðer_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, ðer_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, ðer_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, ðer_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
|
||||
Reference in New Issue
Block a user