diff options
Diffstat (limited to 'emu/NetBSD/ipif.c')
| -rw-r--r-- | emu/NetBSD/ipif.c | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/emu/NetBSD/ipif.c b/emu/NetBSD/ipif.c new file mode 100644 index 00000000..853e2e93 --- /dev/null +++ b/emu/NetBSD/ipif.c @@ -0,0 +1,372 @@ +#include <sys/types.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include "dat.h" +#include "fns.h" +#include "ip.h" +#include "error.h" +#include <sys/ioctl.h> + +int +so_socket(int type) +{ + int fd, one; + + switch(type) { + default: + error("bad protocol type"); + case S_TCP: + type = SOCK_STREAM; + break; + case S_UDP: + type = SOCK_DGRAM; + break; + } + + fd = socket(AF_INET, type, 0); + if(fd < 0) + oserror(); + if(type == SOCK_DGRAM){ + one = 1; + setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (char*)&one, sizeof (one)); + }else{ + one = 1; + setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof(one)); + } + return fd; +} + +int +so_send(int sock, void *va, int len, void *hdr, int hdrlen) +{ + int r; + struct sockaddr sa; + struct sockaddr_in *sin; + char *h = hdr; + + + osenter(); + if(hdr == 0) + r = write(sock, va, len); + else { + memset(&sa, sizeof(sa), 0); + sin = (struct sockaddr_in*)&sa; + sin->sin_family = AF_INET; + switch(hdrlen){ + case OUdphdrlenv4: + memmove(&sin->sin_addr, h, 4); + memmove(&sin->sin_port, h+8, 2); + break; + case OUdphdrlen: + v6tov4((uchar*)&sin->sin_addr, h); + memmove(&sin->sin_port, h+2*IPaddrlen, 2); /* rport */ + break; + default: + v6tov4((uchar*)&sin->sin_addr, h); + memmove(&sin->sin_port, h+3*IPaddrlen, 2); + break; + } + r = sendto(sock, va, len, 0, &sa, sizeof(sa)); + } + osleave(); + return r; +} + +int +so_recv(int sock, void *va, int len, void *hdr, int hdrlen) +{ + int r, l; + struct sockaddr sa; + struct sockaddr_in *sin; + char h[Udphdrlen]; + + + osenter(); + if(hdr == 0) + r = read(sock, va, len); + else { + sin = (struct sockaddr_in*)&sa; + l = sizeof(sa); + r = recvfrom(sock, va, len, 0, &sa, &l); + if(r >= 0) { + memset(h, sizeof h, 0); + switch(hdrlen){ + case OUdphdrlenv4: + memmove(h, &sin->sin_addr, 4); + memmove(h+2*IPv4addrlen, &sin->sin_port, 2); + break; + case OUdphdrlen: + v4tov6(h, (uchar*)&sin->sin_addr); + memmove(h+2*IPaddrlen, &sin->sin_port, 2); + break; + default: + v4tov6(h, (uchar*)&sin->sin_addr); + memmove(h+3*IPaddrlen, &sin->sin_port, 2); + break; + } + + /* alas there's no way to get the local addr/port correctly. Pretend. */ + getsockname(sock, &sa, &l); + switch(hdrlen){ + case OUdphdrlenv4: + memmove(h+IPv4addrlen, &sin->sin_addr, IPv4addrlen); + memmove(h+2*IPv4addrlen+2, &sin->sin_port, 2); + break; + case OUdphdrlen: + v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr); + memmove(h+2*IPaddrlen+2, &sin->sin_port, 2); + break; + default: + v4tov6(h+IPaddrlen, (uchar*)&sin->sin_addr); + v4tov6(h+2*IPaddrlen, (uchar*)&sin->sin_addr); /* ifcaddr */ + memmove(h+3*IPaddrlen+2, &sin->sin_port, 2); + break; + } + memmove(hdr, h, hdrlen); + } + } + osleave(); + return r; +} + +void +so_close(int sock) +{ + close(sock); +} + +void +so_connect(int fd, unsigned long raddr, unsigned short rport) +{ + int r; + struct sockaddr sa; + struct sockaddr_in *sin; + + memset(&sa, 0, sizeof(sa)); + sin = (struct sockaddr_in*)&sa; + sin->sin_family = AF_INET; + hnputs(&sin->sin_port, rport); + hnputl(&sin->sin_addr.s_addr, raddr); + + osenter(); + r = connect(fd, &sa, sizeof(sa)); + osleave(); + if(r < 0) + oserror(); +} + +void +so_getsockname(int fd, unsigned long *laddr, unsigned short *lport) +{ + int len; + struct sockaddr sa; + struct sockaddr_in *sin; + + + len = sizeof(sa); + if(getsockname(fd, &sa, &len) < 0) + oserror(); + + sin = (struct sockaddr_in*)&sa; + if(sin->sin_family != AF_INET || len != sizeof(*sin)) + error("not AF_INET"); + + *laddr = nhgetl(&sin->sin_addr.s_addr); + *lport = nhgets(&sin->sin_port); +} + +void +so_listen(int fd) +{ + int r; + + osenter(); + r = listen(fd, 5); + osleave(); + if(r < 0) + oserror(); +} + +int +so_accept(int fd, unsigned long *raddr, unsigned short *rport) +{ + int nfd, len; + struct sockaddr sa; + struct sockaddr_in *sin; + + sin = (struct sockaddr_in*)&sa; + + len = sizeof(sa); + osenter(); + nfd = accept(fd, &sa, &len); + osleave(); + if(nfd < 0) + oserror(); + + if(sin->sin_family != AF_INET || len != sizeof(*sin)) + error("not AF_INET"); + + *raddr = nhgetl(&sin->sin_addr.s_addr); + *rport = nhgets(&sin->sin_port); + return nfd; +} + +void +so_bind(int fd, int su, unsigned long addr, unsigned short port) +{ + int i, one; + struct sockaddr sa; + struct sockaddr_in *sin; + + sin = (struct sockaddr_in*)&sa; + + one = 1; + if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&one, sizeof(one)) < 0) { + oserrstr(up->genbuf, sizeof(up->genbuf)); + print("setsockopt: %s", up->genbuf); + } + + if(su) { + for(i = 600; i < 1024; i++) { + memset(&sa, 0, sizeof(sa)); + sin->sin_family = AF_INET; + hnputl(&sin->sin_addr.s_addr, addr); + hnputs(&sin->sin_port, i); + + if(bind(fd, &sa, sizeof(sa)) >= 0) + return; + } + oserror(); + } + + memset(&sa, 0, sizeof(sa)); + sin->sin_family = AF_INET; + hnputl(&sin->sin_addr.s_addr, addr); + hnputs(&sin->sin_port, port); + + if(bind(fd, &sa, sizeof(sa)) < 0) + oserror(); +} + +void +so_setsockopt(int fd, int opt, int value) +{ + int r; + struct linger l; + + if(opt == SO_LINGER){ + l.l_onoff = 1; + l.l_linger = (short) value; + osenter(); + r = setsockopt(fd, SOL_SOCKET, opt, (char *)&l, sizeof(l)); + osleave(); + }else + error(Ebadctl); + if(r < 0) + oserror(); +} + +int +so_gethostbyname(char *host, char**hostv, int n) +{ + int i; + char buf[32]; + uchar *p; + struct hostent *hp; + + hp = gethostbyname(host); + if(hp == 0) + return 0; + + for(i = 0; hp->h_addr_list[i] && i < n; i++) { + p = hp->h_addr_list[i]; + snprint(buf, sizeof(buf), "%ud.%ud.%ud.%ud", p[0], p[1], p[2], p[3]); + hostv[i] = strdup(buf); + if(hostv[i] == 0) + break; + } + return i; +} + +int +so_gethostbyaddr(char *addr, char **hostv, int n) +{ + int i; + struct hostent *hp; + unsigned long straddr; + + straddr = inet_addr(addr); + if(straddr == -1) + return 0; + + hp = gethostbyaddr((char *)&straddr, sizeof(straddr), AF_INET); + if(hp == 0) + return 0; + + hostv[0] = strdup(hp->h_name); + if(hostv[0] == 0) + return 0; + for(i = 1; hp->h_aliases[i-1] && i < n; i++) { + hostv[i] = strdup(hp->h_aliases[i-1]); + if(hostv[i] == 0) + break; + } + return i; +} + +int +so_getservbyname(char *service, char *net, char *port) +{ + ushort p; + struct servent *s; + + s = getservbyname(service, net); + if(s == 0) + return -1; + p = s->s_port; + sprint(port, "%d", nhgets(&p)); + return 0; +} + +int +so_hangup(int fd, int linger) +{ + int r; + static struct linger l = {1, 1000}; + + osenter(); + if(linger) + setsockopt(fd, SOL_SOCKET, SO_LINGER, (char*)&l, sizeof(l)); + r = shutdown(fd, 2); + if(r >= 0) + r = close(fd); + osleave(); + return r; +} + +void +arpadd(char *ipaddr, char *eaddr, int n) +{ + error("arp not implemented"); +} + +int +so_mustbind(int restricted, int port) +{ + return restricted || port != 0; +} + +void +so_keepalive(int fd, int ms) +{ + int on; + + USED(ms); + on = 1; + setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)&on, sizeof(on)); +} |
