modem: Transparent IP proxy. Removes the need for a TAP/TUN device

This commit is contained in:
Flyinghead 2018-09-24 19:06:14 +02:00
parent dc5ce8fa8b
commit 2c343fddf2
28 changed files with 936 additions and 7866 deletions

View File

@ -4,7 +4,6 @@
#define PICO_SUPPORT_ETH #define PICO_SUPPORT_ETH
#define PICO_SUPPORT_IPV4 #define PICO_SUPPORT_IPV4
#define PICO_SUPPORT_IPV4FRAG #define PICO_SUPPORT_IPV4FRAG
#define PICO_SUPPORT_ICMP4
#define PICO_SUPPORT_TCP #define PICO_SUPPORT_TCP
#define PICO_SUPPORT_UDP #define PICO_SUPPORT_UDP
#define PICO_SUPPORT_DNS_CLIENT #define PICO_SUPPORT_DNS_CLIENT

View File

@ -38,6 +38,7 @@ struct pico_device {
#ifdef PICO_SUPPORT_IPV6 #ifdef PICO_SUPPORT_IPV6
struct pico_nd_hostvars hostvars; struct pico_nd_hostvars hostvars;
#endif #endif
int proxied;
}; };

View File

@ -92,6 +92,9 @@ struct pico_frame {
uint8_t send_ttl; /* Special TTL/HOPS value, 0 = auto assign */ uint8_t send_ttl; /* Special TTL/HOPS value, 0 = auto assign */
uint8_t send_tos; /* Type of service */ uint8_t send_tos; /* Type of service */
struct pico_ip4 local_ip; /* address to masquerade */
uint16_t local_port; /* port to masquerade */
}; };
/** frame alloc/dealloc/copy **/ /** frame alloc/dealloc/copy **/

View File

@ -175,6 +175,8 @@ struct pico_msginfo {
struct pico_device *dev; struct pico_device *dev;
uint8_t ttl; uint8_t ttl;
uint8_t tos; uint8_t tos;
union pico_address local_addr;
uint16_t local_port;
}; };
struct pico_socket *pico_socket_open(uint16_t net, uint16_t proto, void (*wakeup)(uint16_t ev, struct pico_socket *s)); struct pico_socket *pico_socket_open(uint16_t net, uint16_t proto, void (*wakeup)(uint16_t ev, struct pico_socket *s));

View File

@ -1,231 +0,0 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Daniele Lacamera
*********************************************************************/
#ifndef _WIN32
#include <fcntl.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <signal.h>
#include "pico_device.h"
#include "pico_dev_tap.h"
#include "pico_stack.h"
#ifndef __FreeBSD__
#include <linux/if_tun.h>
#endif
#include <sys/poll.h>
struct pico_device_tap {
struct pico_device dev;
int fd;
};
#define TUN_MTU 2048
/* We only support one global link state - we only have two USR signals, we */
/* can't spread these out over an arbitrary amount of devices. When you unplug */
/* one tap, you unplug all of them. */
static int tapdev_link_state = 0;
static void sig_handler(int signo)
{
if (signo == SIGUSR1) {
tapdev_link_state = 0;
}
if (signo == SIGUSR2) {
tapdev_link_state = 1;
}
}
static int tap_link_state(__attribute__((unused)) struct pico_device *self)
{
return tapdev_link_state;
}
static int pico_tap_send(struct pico_device *dev, void *buf, int len)
{
struct pico_device_tap *tap = (struct pico_device_tap *) dev;
return (int)write(tap->fd, buf, (uint32_t)len);
}
static int pico_tap_poll(struct pico_device *dev, int loop_score)
{
struct pico_device_tap *tap = (struct pico_device_tap *) dev;
struct pollfd pfd;
unsigned char buf[TUN_MTU];
int len;
pfd.fd = tap->fd;
pfd.events = POLLIN;
do {
if (poll(&pfd, 1, 0) <= 0) {
return loop_score;
}
len = (int)read(tap->fd, buf, TUN_MTU);
if (len > 0) {
loop_score--;
pico_stack_recv(dev, buf, (uint32_t)len);
}
} while(loop_score > 0);
return 0;
}
/* Public interface: create/destroy. */
void pico_tap_destroy(struct pico_device *dev)
{
struct pico_device_tap *tap = (struct pico_device_tap *) dev;
if(tap->fd > 0) {
close(tap->fd);
}
}
#ifndef __FreeBSD__
static int tap_open(const char *name)
{
struct ifreq ifr;
int tap_fd;
if((tap_fd = open("/dev/net/tun", O_RDWR)) < 0) {
return -1;
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
strncpy(ifr.ifr_name, name, IFNAMSIZ);
if(ioctl(tap_fd, TUNSETIFF, &ifr) < 0) {
return -1;
}
return tap_fd;
}
#else
static int tap_open(const char *name)
{
int tap_fd;
(void)name;
tap_fd = open("/dev/tap0", O_RDWR);
return tap_fd;
}
#endif
#ifndef __FreeBSD__
static int tap_get_mac(const char *name, uint8_t *mac)
{
int sck;
struct ifreq eth;
int retval = -1;
sck = socket(AF_INET, SOCK_DGRAM, 0);
if(sck < 0) {
return retval;
}
memset(&eth, 0, sizeof(struct ifreq));
strcpy(eth.ifr_name, name);
/* call the IOCTL */
if (ioctl(sck, SIOCGIFHWADDR, &eth) < 0) {
perror("ioctl(SIOCGIFHWADDR)");
return -1;
}
memcpy (mac, &eth.ifr_hwaddr.sa_data, 6);
close(sck);
return 0;
}
#else
#include <net/if_dl.h>
#include <ifaddrs.h>
#include <net/if_types.h>
static int tap_get_mac(const char *name, uint8_t *mac)
{
struct sockaddr_dl *sdl;
struct ifaddrs *ifap, *root;
if (getifaddrs(&ifap) != 0)
return -1;
root = ifap;
while(ifap) {
if (strcmp(name, ifap->ifa_name) == 0) {
sdl = (struct sockaddr_dl *) ifap->ifa_addr;
}
if (sdl->sdl_type == IFT_ETHER) {
memcpy(mac, LLADDR(sdl), 6);
freeifaddrs(root);
return 0;
}
ifap = ifap->ifa_next;
}
freeifaddrs(root);
return 0;
}
#endif
struct pico_device *pico_tap_create(const char *name)
{
struct pico_device_tap *tap = PICO_ZALLOC(sizeof(struct pico_device_tap));
uint8_t mac[6] = {};
struct sigaction sa;
if (!tap) {
return NULL;
}
sa.sa_flags = 0;
sigemptyset(&sa.sa_mask);
sa.sa_handler = sig_handler;
if ((sigaction(SIGUSR1, &sa, NULL) == 0) &&
(sigaction(SIGUSR2, &sa, NULL) == 0)) {
tap->dev.link_state = &tap_link_state;
}
tap->dev.overhead = 0;
tap->fd = tap_open(name);
if (tap->fd < 0) {
dbg("Tap creation failed.\n");
pico_tap_destroy((struct pico_device *)tap);
return NULL;
}
/* Host's mac address is generated * by the host kernel and is
* retrieved via tap_get_mac().
*/
if (tap_get_mac(name, mac) < 0) {
dbg("Tap mac query failed.\n");
pico_tap_destroy((struct pico_device *)tap);
return NULL;
}
/* To act as a second endpoint in the same subnet, the picoTCP
* app using the tap device must have a different mac address.
* For simplicity, we just add 1 to the last byte of the linux
* endpoint so the two addresses are consecutive.
*/
mac[5]++;
if( 0 != pico_device_init((struct pico_device *)tap, name, mac)) {
dbg("Tap init failed.\n");
pico_tap_destroy((struct pico_device *)tap);
return NULL;
}
tap->dev.send = pico_tap_send;
tap->dev.poll = pico_tap_poll;
tap->dev.destroy = pico_tap_destroy;
dbg("Device %s created.\n", tap->dev.name);
return (struct pico_device *)tap;
}
#endif

View File

@ -1,16 +0,0 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
*********************************************************************/
#ifndef _WIN32
#ifndef INCLUDE_PICO_TAP
#define INCLUDE_PICO_TAP
#include "pico_config.h"
#include "pico_device.h"
void pico_tap_destroy(struct pico_device *tap);
struct pico_device *pico_tap_create(const char *name);
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +0,0 @@
/*********************************************************************
PicoTCP. Copyright (c) 2014-2017 Altran Intelligent Systems. Some rights reserved.
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
*********************************************************************/
#ifdef _WIN32
#ifndef INCLUDE_PICO_TAP
#define INCLUDE_PICO_TAP
#include "pico_config.h"
#include "pico_device.h"
/* will look for the first TAP device available, and use it */
struct pico_device *pico_tap_create(char *name, uint8_t *mac);
/* TODO: not implemented yet */
/* void pico_tap_destroy(struct pico_device *null); */
const char *pico_tap_get_guid(struct pico_device *dev);
#endif
#endif

View File

@ -1,89 +0,0 @@
/*********************************************************************
PicoTCP. Copyright (c) 2014-2017 Altran Intelligent Systems. Some rights reserved.
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Maxime Vincent
Based on the OpenVPN tun.c driver, under GPL
NOTES: This is the Windows-only driver, a Linux-equivalent is available, too
You need to have an OpenVPN TUN/TAP network adapter installed, first
This driver is barely working:
* Only TAP-mode is supported (TUN is not)
* it will simply open the first TAP device it can find
* there is memory being allocated that's never freed
* there is no destroy function, yet
* it has only been tested on a Windows 7 machine
*********************************************************************/
#ifndef __PICO_DEV_TAP_WINDOWS_PRIVATE_H
#define __PICO_DEV_TAP_WINDOWS_PRIVATE_H
/* Extra defines (vnz) */
#define TAP_WIN_COMPONENT_ID "tap0901"
#define TAP_WIN_MIN_MAJOR 9
#define TAP_WIN_MIN_MINOR 9
#define PACKAGE_NAME "PicoTCP WinTAP"
/* Extra structs */
struct tap_reg
{
const char *guid;
struct tap_reg *next;
};
struct panel_reg
{
const char *name;
const char *guid;
struct panel_reg *next;
};
/*
* =============
* TAP IOCTLs
* =============
*/
#define TAP_WIN_CONTROL_CODE(request, method) \
CTL_CODE (FILE_DEVICE_UNKNOWN, request, method, FILE_ANY_ACCESS)
/* Present in 8.1 */
#define TAP_WIN_IOCTL_GET_MAC TAP_WIN_CONTROL_CODE (1, METHOD_BUFFERED)
#define TAP_WIN_IOCTL_GET_VERSION TAP_WIN_CONTROL_CODE (2, METHOD_BUFFERED)
#define TAP_WIN_IOCTL_GET_MTU TAP_WIN_CONTROL_CODE (3, METHOD_BUFFERED)
#define TAP_WIN_IOCTL_GET_INFO TAP_WIN_CONTROL_CODE (4, METHOD_BUFFERED)
#define TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT TAP_WIN_CONTROL_CODE (5, METHOD_BUFFERED)
#define TAP_WIN_IOCTL_SET_MEDIA_STATUS TAP_WIN_CONTROL_CODE (6, METHOD_BUFFERED)
#define TAP_WIN_IOCTL_CONFIG_DHCP_MASQ TAP_WIN_CONTROL_CODE (7, METHOD_BUFFERED)
#define TAP_WIN_IOCTL_GET_LOG_LINE TAP_WIN_CONTROL_CODE (8, METHOD_BUFFERED)
#define TAP_WIN_IOCTL_CONFIG_DHCP_SET_OPT TAP_WIN_CONTROL_CODE (9, METHOD_BUFFERED)
/* Added in 8.2 */
/* obsoletes TAP_WIN_IOCTL_CONFIG_POINT_TO_POINT */
#define TAP_WIN_IOCTL_CONFIG_TUN TAP_WIN_CONTROL_CODE (10, METHOD_BUFFERED)
/*
* =================
* Registry keys
* =================
*/
#define ADAPTER_KEY "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
#define NETWORK_CONNECTIONS_KEY "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"
/*
* ======================
* Filesystem prefixes
* ======================
*/
#define USERMODEDEVICEDIR "\\\\.\\Global\\"
#define SYSDEVICEDIR "\\Device\\"
#define USERDEVICEDIR "\\DosDevices\\Global\\"
#define TAP_WIN_SUFFIX ".tap"
#endif

View File

@ -1,218 +0,0 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
Authors: Daniele Lacamera
*********************************************************************/
#ifndef _WIN32
#include <fcntl.h>
#include <sys/ioctl.h>
#include <net/if.h>
#ifdef __APPLE__
#include <sys/sys_domain.h>
#include <net/if_utun.h> // UTUN_CONTROL_NAME
#include <sys/kern_control.h> // struct socketaddr_ctl
#else
#include <linux/if_tun.h>
#endif
#include "pico_device.h"
#include "pico_dev_tun.h"
#include "pico_stack.h"
#include <sys/poll.h>
struct pico_device_tun {
struct pico_device dev;
int fd;
};
#define TUN_MTU 2048
static int pico_tun_send(struct pico_device *dev, void *buf, int len)
{
struct pico_device_tun *tun = (struct pico_device_tun *) dev;
#ifdef __APPLE__
// Add the protocol (IP) before the packet (4 bytes)
uint8_t *p = (uint8_t *)malloc(len + 4);
*(uint32_t *)p = 2;
memcpy(p + 4, buf, len);
int rc = (int)write(tun->fd, p, len + 4);
free(p);
return rc;
#endif
return (int)write(tun->fd, buf, (uint32_t)len);
}
static int pico_tun_poll(struct pico_device *dev, int loop_score)
{
struct pico_device_tun *tun = (struct pico_device_tun *) dev;
struct pollfd pfd;
unsigned char buf[TUN_MTU];
int len;
pfd.fd = tun->fd;
pfd.events = POLLIN;
do {
if (poll(&pfd, 1, 0) <= 0)
return loop_score;
len = (int)read(tun->fd, buf, TUN_MTU);
if (len > 0) {
loop_score--;
#ifdef __APPLE__
pico_stack_recv(dev, buf + 4, (uint32_t)len - 4);
#else
pico_stack_recv(dev, buf, (uint32_t)len);
#endif
}
} while(loop_score > 0);
return 0;
}
/* Public interface: create/destroy. */
void pico_tun_destroy(struct pico_device *dev)
{
struct pico_device_tun *tun = (struct pico_device_tun *) dev;
if(tun->fd > 0)
close(tun->fd);
}
#ifdef IFF_TUN // Linux
static int tun_open(const char *name)
{
struct ifreq ifr;
int tun_fd;
if((tun_fd = open("/dev/net/tun", O_RDWR)) < 0) {
return(-1);
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
strncpy(ifr.ifr_name, name, IFNAMSIZ);
if(ioctl(tun_fd, TUNSETIFF, &ifr) < 0) {
return(-1);
}
return tun_fd;
}
#else // BSD, OS X, ...
#ifdef __APPLE__
static int tun_open(const char *name)
{
struct ctl_info ctlInfo;
strlcpy(ctlInfo.ctl_name, UTUN_CONTROL_NAME, sizeof(ctlInfo.ctl_name));
int fd = -1;
for (int unit = 0; unit < 256 && fd == -1; unit++)
{
fd = socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL);
if (fd < 0) {
perror("socket");
continue;
}
struct sockaddr_ctl sc;
if (ioctl(fd, CTLIOCGINFO, &ctlInfo) == -1) {
close(fd);
perror("ioctl");
fd = -1;
continue;
}
printf("ctl_info: {ctl_id: %ud, ctl_name: %s}\n",
ctlInfo.ctl_id, ctlInfo.ctl_name);
sc.sc_id = ctlInfo.ctl_id;
sc.sc_len = sizeof(sc);
sc.sc_family = AF_SYSTEM;
sc.ss_sysaddr = AF_SYS_CONTROL;
sc.sc_unit = unit;
if (connect(fd, (struct sockaddr *)&sc, sizeof(sc)) < 0) {
perror("connect");
close(fd);
fd = -1;
continue;
}
printf("Opened tunnel utun%d\n", unit);
// set_nonblock (fd);
fcntl (fd, F_SETFL, O_NONBLOCK);
// set_cloexec (fd);
/*
int s = socket(PF_ROUTE, SOCK_RAW, 0);
af = AF_INET;
aflen = sizeof(struct sockaddr_in);
flags |= RTF_UP;
flags |= RTF_HOST;
if ((ret = rtmsg(*cmd, flags)) == 0)
break;
*/
}
return fd;
}
#elif defined(SIOCIFCREATE)
static int tun_open(const char *name)
{
int fd;
int s;
struct ifreq ifr;
fd = open(name, O_RDWR);
if (fd == -1)
{
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0)
return -1;
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, name + 5, sizeof(ifr.ifr_name) - 1);
if (!ioctl(s, SIOCIFCREATE, &ifr))
fd = open(name, O_RDWR);
close(s);
}
return fd;
}
#else
#define tun_open(tun_name) open(tun_name, O_RDWR)
#endif
#endif
struct pico_device *pico_tun_create(const char *name)
{
struct pico_device_tun *tun = PICO_ZALLOC(sizeof(struct pico_device_tun));
if (!tun)
return NULL;
if( 0 != pico_device_init((struct pico_device *)tun, name, NULL)) {
printf("Tun init failed.\n");
pico_tun_destroy((struct pico_device *)tun);
return NULL;
}
tun->dev.overhead = 0;
tun->fd = tun_open(name);
if (tun->fd < 0) {
printf("Tun creation failed.\n");
pico_tun_destroy((struct pico_device *)tun);
return NULL;
}
tun->dev.send = pico_tun_send;
tun->dev.poll = pico_tun_poll;
tun->dev.destroy = pico_tun_destroy;
dbg("Device %s created.\n", tun->dev.name);
return (struct pico_device *)tun;
}
#endif

View File

@ -1,17 +0,0 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
*********************************************************************/
#ifndef _WIN32
#ifndef INCLUDE_PICO_TUN
#define INCLUDE_PICO_TUN
#include "pico_config.h"
#include "pico_device.h"
void pico_tun_destroy(struct pico_device *tun);
struct pico_device *pico_tun_create(const char *name);
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,190 +0,0 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
Authors: Frederik Van Slycken
*********************************************************************/
#include "pico_config.h"
#include "pico_stack.h"
#include "pico_dhcp_common.h"
#if defined (PICO_SUPPORT_DHCPC) || defined (PICO_SUPPORT_DHCPD)
/* pico_dhcp_are_options_valid needs to be called first to prevent illegal memory access */
/* The argument pointer is moved forward to the next option */
struct pico_dhcp_opt *pico_dhcp_next_option(struct pico_dhcp_opt **ptr)
{
uint8_t **p = (uint8_t **)ptr;
struct pico_dhcp_opt *opt = *ptr;
if (opt->code == PICO_DHCP_OPT_END)
return NULL;
if (opt->code == PICO_DHCP_OPT_PAD) {
*p += 1;
return *ptr;
}
*p += (opt->len + 2); /* (len + 2) to account for code and len octet */
return *ptr;
}
uint8_t pico_dhcp_are_options_valid(void *ptr, int32_t len)
{
uint8_t optlen = 0, *p = ptr;
while (len > 0) {
switch (*p)
{
case PICO_DHCP_OPT_END:
return 1;
case PICO_DHCP_OPT_PAD:
p++;
len--;
break;
default:
p++; /* move pointer from code octet to len octet */
len--;
if ((len <= 0) || (len - (*p + 1) < 0)) /* (*p + 1) to account for len octet */
return 0;
optlen = *p;
p += optlen + 1;
len -= optlen;
break;
}
}
return 0;
}
uint8_t pico_dhcp_opt_netmask(void *ptr, struct pico_ip4 *ip)
{
struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
/* option: netmask */
opt->code = PICO_DHCP_OPT_NETMASK;
opt->len = PICO_DHCP_OPTLEN_NETMASK - PICO_DHCP_OPTLEN_HDR;
opt->ext.netmask.ip = *ip;
return PICO_DHCP_OPTLEN_NETMASK;
}
uint8_t pico_dhcp_opt_router(void *ptr, struct pico_ip4 *ip)
{
struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
/* option: router */
opt->code = PICO_DHCP_OPT_ROUTER;
opt->len = PICO_DHCP_OPTLEN_ROUTER - PICO_DHCP_OPTLEN_HDR;
opt->ext.router.ip = *ip;
return PICO_DHCP_OPTLEN_ROUTER;
}
uint8_t pico_dhcp_opt_dns(void *ptr, struct pico_ip4 *ip)
{
struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
/* option: dns */
opt->code = PICO_DHCP_OPT_DNS;
opt->len = PICO_DHCP_OPTLEN_DNS - PICO_DHCP_OPTLEN_HDR;
opt->ext.dns1.ip = *ip;
return PICO_DHCP_OPTLEN_DNS;
}
uint8_t pico_dhcp_opt_broadcast(void *ptr, struct pico_ip4 *ip)
{
struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
/* option: broadcast */
opt->code = PICO_DHCP_OPT_BROADCAST;
opt->len = PICO_DHCP_OPTLEN_BROADCAST - PICO_DHCP_OPTLEN_HDR;
opt->ext.broadcast.ip = *ip;
return PICO_DHCP_OPTLEN_BROADCAST;
}
uint8_t pico_dhcp_opt_reqip(void *ptr, struct pico_ip4 *ip)
{
struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
/* option: request IP address */
opt->code = PICO_DHCP_OPT_REQIP;
opt->len = PICO_DHCP_OPTLEN_REQIP - PICO_DHCP_OPTLEN_HDR;
opt->ext.req_ip.ip = *ip;
return PICO_DHCP_OPTLEN_REQIP;
}
uint8_t pico_dhcp_opt_leasetime(void *ptr, uint32_t time)
{
struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
/* option: lease time */
opt->code = PICO_DHCP_OPT_LEASETIME;
opt->len = PICO_DHCP_OPTLEN_LEASETIME - PICO_DHCP_OPTLEN_HDR;
opt->ext.lease_time.time = time;
return PICO_DHCP_OPTLEN_LEASETIME;
}
uint8_t pico_dhcp_opt_msgtype(void *ptr, uint8_t type)
{
struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
/* option: message type */
opt->code = PICO_DHCP_OPT_MSGTYPE;
opt->len = PICO_DHCP_OPTLEN_MSGTYPE - PICO_DHCP_OPTLEN_HDR;
opt->ext.msg_type.type = type;
return PICO_DHCP_OPTLEN_MSGTYPE;
}
uint8_t pico_dhcp_opt_serverid(void *ptr, struct pico_ip4 *ip)
{
struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
/* option: server identifier */
opt->code = PICO_DHCP_OPT_SERVERID;
opt->len = PICO_DHCP_OPTLEN_SERVERID - PICO_DHCP_OPTLEN_HDR;
opt->ext.server_id.ip = *ip;
return PICO_DHCP_OPTLEN_SERVERID;
}
uint8_t pico_dhcp_opt_paramlist(void *ptr)
{
struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
uint8_t *param_code = &(opt->ext.param_list.code[0]);
/* option: parameter list */
opt->code = PICO_DHCP_OPT_PARAMLIST;
opt->len = PICO_DHCP_OPTLEN_PARAMLIST - PICO_DHCP_OPTLEN_HDR;
param_code[0] = PICO_DHCP_OPT_NETMASK;
param_code[1] = PICO_DHCP_OPT_TIME;
param_code[2] = PICO_DHCP_OPT_ROUTER;
param_code[3] = PICO_DHCP_OPT_HOSTNAME;
param_code[4] = PICO_DHCP_OPT_RENEWALTIME;
param_code[5] = PICO_DHCP_OPT_REBINDINGTIME;
param_code[6] = PICO_DHCP_OPT_DNS;
return PICO_DHCP_OPTLEN_PARAMLIST;
}
uint8_t pico_dhcp_opt_maxmsgsize(void *ptr, uint16_t size)
{
struct pico_dhcp_opt *opt = (struct pico_dhcp_opt *)ptr;
/* option: maximum message size */
opt->code = PICO_DHCP_OPT_MAXMSGSIZE;
opt->len = PICO_DHCP_OPTLEN_MAXMSGSIZE - PICO_DHCP_OPTLEN_HDR;
opt->ext.max_msg_size.size = short_be(size);
return PICO_DHCP_OPTLEN_MAXMSGSIZE;
}
uint8_t pico_dhcp_opt_end(void *ptr)
{
uint8_t *opt = (uint8_t *)ptr;
/* option: end of options */
*opt = PICO_DHCP_OPT_END;
return PICO_DHCP_OPTLEN_END;
}
#endif

View File

@ -1,434 +0,0 @@
/*********************************************************************
PicoTCP. Copyright (c) 2012-2017 Altran Intelligent Systems. Some rights reserved.
See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
.
Authors: Daniele Lacamera
*********************************************************************/
#include "pico_icmp4.h"
#include "pico_config.h"
#include "pico_ipv4.h"
#include "pico_eth.h"
#include "pico_device.h"
#include "pico_stack.h"
#include "pico_tree.h"
/* Queues */
static struct pico_queue icmp_in = {
0
};
static struct pico_queue icmp_out = {
0
};
/* Functions */
static int pico_icmp4_checksum(struct pico_frame *f)
{
struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
if (!hdr) {
pico_err = PICO_ERR_EINVAL;
return -1;
}
hdr->crc = 0;
hdr->crc = short_be(pico_checksum(hdr, f->transport_len));
return 0;
}
#ifdef PICO_SUPPORT_PING
static void ping_recv_reply(struct pico_frame *f);
#endif
static int pico_icmp4_process_in(struct pico_protocol *self, struct pico_frame *f)
{
struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
static int firstpkt = 1;
static uint16_t last_id = 0;
static uint16_t last_seq = 0;
IGNORE_PARAMETER(self);
if (hdr->type == PICO_ICMP_ECHO) {
hdr->type = PICO_ICMP_ECHOREPLY;
/* outgoing frames require a f->len without the ethernet header len */
if (f->dev && f->dev->eth)
f->len -= PICO_SIZE_ETHHDR;
if (!firstpkt && (hdr->hun.ih_idseq.idseq_id == last_id) && (last_seq == hdr->hun.ih_idseq.idseq_seq)) {
/* The network duplicated the echo. Do not reply. */
pico_frame_discard(f);
return 0;
}
firstpkt = 0;
last_id = hdr->hun.ih_idseq.idseq_id;
last_seq = hdr->hun.ih_idseq.idseq_seq;
pico_icmp4_checksum(f);
pico_ipv4_rebound(f);
} else if (hdr->type == PICO_ICMP_UNREACH) {
f->net_hdr = f->transport_hdr + PICO_ICMPHDR_UN_SIZE;
pico_ipv4_unreachable(f, hdr->code);
} else if (hdr->type == PICO_ICMP_ECHOREPLY) {
#ifdef PICO_SUPPORT_PING
ping_recv_reply(f);
#endif
pico_frame_discard(f);
} else {
pico_frame_discard(f);
}
return 0;
}
static int pico_icmp4_process_out(struct pico_protocol *self, struct pico_frame *f)
{
IGNORE_PARAMETER(self);
IGNORE_PARAMETER(f);
dbg("Called %s\n", __FUNCTION__);
return 0;
}
/* Interface: protocol definition */
struct pico_protocol pico_proto_icmp4 = {
.name = "icmp4",
.proto_number = PICO_PROTO_ICMP4,
.layer = PICO_LAYER_TRANSPORT,
.process_in = pico_icmp4_process_in,
.process_out = pico_icmp4_process_out,
.q_in = &icmp_in,
.q_out = &icmp_out,
};
static int pico_icmp4_notify(struct pico_frame *f, uint8_t type, uint8_t code)
{
struct pico_frame *reply;
struct pico_icmp4_hdr *hdr;
struct pico_ipv4_hdr *info;
uint16_t f_tot_len;
if (f == NULL) {
pico_err = PICO_ERR_EINVAL;
return -1;
}
f_tot_len = short_be(((struct pico_ipv4_hdr *)f->net_hdr)->len);
if (f_tot_len < (sizeof(struct pico_ipv4_hdr)))
return -1;
/* Truncate tot len to be at most 8 bytes + iphdr */
if (f_tot_len > (sizeof(struct pico_ipv4_hdr) + 8u)) {
f_tot_len = (sizeof(struct pico_ipv4_hdr) + 8u);
}
reply = pico_proto_ipv4.alloc(&pico_proto_ipv4, f->dev, (uint16_t) (f_tot_len + PICO_ICMPHDR_UN_SIZE));
info = (struct pico_ipv4_hdr*)(f->net_hdr);
hdr = (struct pico_icmp4_hdr *) reply->transport_hdr;
hdr->type = type;
hdr->code = code;
hdr->hun.ih_pmtu.ipm_nmtu = short_be(1500);
hdr->hun.ih_pmtu.ipm_void = 0;
reply->transport_len = (uint16_t)(f_tot_len + PICO_ICMPHDR_UN_SIZE);
reply->payload = reply->transport_hdr + PICO_ICMPHDR_UN_SIZE;
memcpy(reply->payload, f->net_hdr, f_tot_len);
pico_icmp4_checksum(reply);
pico_ipv4_frame_push(reply, &info->src, PICO_PROTO_ICMP4);
return 0;
}
int pico_icmp4_port_unreachable(struct pico_frame *f)
{
/*Parameter check executed in pico_icmp4_notify*/
return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_PORT);
}
int pico_icmp4_proto_unreachable(struct pico_frame *f)
{
/*Parameter check executed in pico_icmp4_notify*/
return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_PROTOCOL);
}
int pico_icmp4_dest_unreachable(struct pico_frame *f)
{
/*Parameter check executed in pico_icmp4_notify*/
return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_HOST);
}
int pico_icmp4_ttl_expired(struct pico_frame *f)
{
/*Parameter check executed in pico_icmp4_notify*/
return pico_icmp4_notify(f, PICO_ICMP_TIME_EXCEEDED, PICO_ICMP_TIMXCEED_INTRANS);
}
MOCKABLE int pico_icmp4_frag_expired(struct pico_frame *f)
{
/*Parameter check executed in pico_icmp4_notify*/
return pico_icmp4_notify(f, PICO_ICMP_TIME_EXCEEDED, PICO_ICMP_TIMXCEED_REASS);
}
int pico_icmp4_mtu_exceeded(struct pico_frame *f)
{
/*Parameter check executed in pico_icmp4_notify*/
return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_NEEDFRAG);
}
int pico_icmp4_packet_filtered(struct pico_frame *f)
{
/*Parameter check executed in pico_icmp4_notify*/
/*Packet Filtered: type 3, code 13 (Communication Administratively Prohibited)*/
return pico_icmp4_notify(f, PICO_ICMP_UNREACH, PICO_ICMP_UNREACH_FILTER_PROHIB);
}
int pico_icmp4_param_problem(struct pico_frame *f, uint8_t code)
{
return pico_icmp4_notify(f, PICO_ICMP_PARAMPROB, code);
}
/***********************/
/* Ping implementation */
/***********************/
/***********************/
/***********************/
/***********************/
#ifdef PICO_SUPPORT_PING
struct pico_icmp4_ping_cookie
{
struct pico_ip4 dst;
uint16_t err;
uint16_t id;
uint16_t seq;
uint16_t size;
int count;
pico_time timestamp;
int interval;
int timeout;
void (*cb)(struct pico_icmp4_stats*);
};
static int cookie_compare(void *ka, void *kb)
{
struct pico_icmp4_ping_cookie *a = ka, *b = kb;
if (a->id < b->id)
return -1;
if (a->id > b->id)
return 1;
return (a->seq - b->seq);
}
static PICO_TREE_DECLARE(Pings, cookie_compare);
static int8_t pico_icmp4_send_echo(struct pico_icmp4_ping_cookie *cookie)
{
struct pico_frame *echo = NULL;
struct pico_icmp4_hdr *hdr;
struct pico_device *dev = pico_ipv4_source_dev_find(&cookie->dst);
if (!dev)
return -1;
echo = pico_proto_ipv4.alloc(&pico_proto_ipv4, dev, (uint16_t)(PICO_ICMPHDR_UN_SIZE + cookie->size));
if (!echo)
return -1;
hdr = (struct pico_icmp4_hdr *) echo->transport_hdr;
hdr->type = PICO_ICMP_ECHO;
hdr->code = 0;
hdr->hun.ih_idseq.idseq_id = short_be(cookie->id);
hdr->hun.ih_idseq.idseq_seq = short_be(cookie->seq);
echo->transport_len = (uint16_t)(PICO_ICMPHDR_UN_SIZE + cookie->size);
echo->payload = echo->transport_hdr + PICO_ICMPHDR_UN_SIZE;
echo->payload_len = cookie->size;
/* XXX: Fill payload */
pico_icmp4_checksum(echo);
pico_ipv4_frame_push(echo, &cookie->dst, PICO_PROTO_ICMP4);
return 0;
}
static void ping_timeout(pico_time now, void *arg)
{
struct pico_icmp4_ping_cookie *cookie = (struct pico_icmp4_ping_cookie *)arg;
IGNORE_PARAMETER(now);
if(pico_tree_findKey(&Pings, cookie)) {
if (cookie->err == PICO_PING_ERR_PENDING) {
struct pico_icmp4_stats stats;
stats.dst = cookie->dst;
stats.seq = cookie->seq;
stats.time = 0;
stats.size = cookie->size;
stats.err = PICO_PING_ERR_TIMEOUT;
dbg(" ---- Ping timeout!!!\n");
cookie->cb(&stats);
}
pico_tree_delete(&Pings, cookie);
PICO_FREE(cookie);
}
}
static void next_ping(pico_time now, void *arg);
static int send_ping(struct pico_icmp4_ping_cookie *cookie)
{
uint32_t timeout_timer = 0;
struct pico_icmp4_stats stats;
pico_icmp4_send_echo(cookie);
cookie->timestamp = pico_tick;
timeout_timer = pico_timer_add((uint32_t)cookie->timeout, ping_timeout, cookie);
if (!timeout_timer) {
goto fail;
}
if (cookie->seq < (uint16_t)cookie->count) {
if (!pico_timer_add((uint32_t)cookie->interval, next_ping, cookie)) {
pico_timer_cancel(timeout_timer);
goto fail;
}
}
return 0;
fail:
dbg("ICMP4: Failed to start timer\n");
cookie->err = PICO_PING_ERR_ABORTED;
stats.err = cookie->err;
cookie->cb(&stats);
pico_tree_delete(&Pings, cookie);
return -1;
}
static void next_ping(pico_time now, void *arg)
{
struct pico_icmp4_ping_cookie *newcookie, *cookie = (struct pico_icmp4_ping_cookie *)arg;
IGNORE_PARAMETER(now);
if(pico_tree_findKey(&Pings, cookie)) {
if (cookie->err == PICO_PING_ERR_ABORTED)
return;
if (cookie->seq < (uint16_t)cookie->count) {
newcookie = PICO_ZALLOC(sizeof(struct pico_icmp4_ping_cookie));
if (!newcookie)
return;
memcpy(newcookie, cookie, sizeof(struct pico_icmp4_ping_cookie));
newcookie->seq++;
if (pico_tree_insert(&Pings, newcookie)) {
dbg("ICMP4: Failed to insert new cookie in tree \n");
PICO_FREE(newcookie);
return;
}
if (send_ping(newcookie)) {
dbg("ICMP4: Failed to send ping\n");
PICO_FREE(newcookie);
}
}
}
}
static void ping_recv_reply(struct pico_frame *f)
{
struct pico_icmp4_ping_cookie test, *cookie;
struct pico_icmp4_hdr *hdr = (struct pico_icmp4_hdr *) f->transport_hdr;
test.id = short_be(hdr->hun.ih_idseq.idseq_id );
test.seq = short_be(hdr->hun.ih_idseq.idseq_seq);
cookie = pico_tree_findKey(&Pings, &test);
if (cookie) {
struct pico_icmp4_stats stats;
if (cookie->err == PICO_PING_ERR_ABORTED)
return;
cookie->err = PICO_PING_ERR_REPLIED;
stats.dst = ((struct pico_ipv4_hdr *)f->net_hdr)->src;
stats.seq = cookie->seq;
stats.size = cookie->size;
stats.time = pico_tick - cookie->timestamp;
stats.err = cookie->err;
stats.ttl = ((struct pico_ipv4_hdr *)f->net_hdr)->ttl;
if(cookie->cb != NULL)
cookie->cb(&stats);
} else {
dbg("Reply for seq=%d, not found.\n", test.seq);
}
}
int pico_icmp4_ping(char *dst, int count, int interval, int timeout, int size, void (*cb)(struct pico_icmp4_stats *))
{
static uint16_t next_id = 0x91c0;
struct pico_icmp4_ping_cookie *cookie;
if((dst == NULL) || (interval == 0) || (timeout == 0) || (count == 0)) {
pico_err = PICO_ERR_EINVAL;
return -1;
}
cookie = PICO_ZALLOC(sizeof(struct pico_icmp4_ping_cookie));
if (!cookie) {
pico_err = PICO_ERR_ENOMEM;
return -1;
}
if (pico_string_to_ipv4(dst, (uint32_t *)&cookie->dst.addr) < 0) {
pico_err = PICO_ERR_EINVAL;
PICO_FREE(cookie);
return -1;
}
cookie->seq = 1;
cookie->id = next_id++;
cookie->err = PICO_PING_ERR_PENDING;
cookie->size = (uint16_t)size;
cookie->interval = interval;
cookie->timeout = timeout;
cookie->cb = cb;
cookie->count = count;
if (pico_tree_insert(&Pings, cookie)) {
dbg("ICMP4: Failed to insert cookie in tree \n");
PICO_FREE(cookie);
return -1;
}
if (send_ping(cookie)) {
PICO_FREE(cookie);
return -1;
}
return cookie->id;
}
int pico_icmp4_ping_abort(int id)
{
struct pico_tree_node *node;
int found = 0;
pico_tree_foreach(node, &Pings)
{
struct pico_icmp4_ping_cookie *ck =
(struct pico_icmp4_ping_cookie *) node->keyValue;
if (ck->id == (uint16_t)id) {
ck->err = PICO_PING_ERR_ABORTED;
found++;
}
}
if (found > 0)
return 0; /* OK if at least one pending ping has been canceled */
pico_err = PICO_ERR_ENOENT;
return -1;
}
#endif

View File

@ -349,6 +349,11 @@ static int pico_ipv4_process_local_unicast_in(struct pico_frame *f)
else else
pico_transport_receive(f, hdr->proto); pico_transport_receive(f, hdr->proto);
return 1;
} else if (f->dev->proxied) {
// Proxied device: deliver all traffic locally
pico_transport_receive(f, hdr->proto);
return 1; return 1;
} else if (pico_tree_findKey(&Tree_dev_link, &test)) { } else if (pico_tree_findKey(&Tree_dev_link, &test)) {
#ifdef PICO_SUPPORT_UDP #ifdef PICO_SUPPORT_UDP
@ -1052,7 +1057,10 @@ int pico_ipv4_frame_push(struct pico_frame *f, struct pico_ip4 *dst, uint8_t pro
} }
hdr->dst.addr = dst->addr; hdr->dst.addr = dst->addr;
hdr->src.addr = link->address.addr; if (f->local_ip.addr)
hdr->src.addr = f->local_ip.addr; // Masqueraded
else
hdr->src.addr = link->address.addr;
hdr->ttl = ttl; hdr->ttl = ttl;
hdr->tos = f->send_tos; hdr->tos = f->send_tos;
hdr->proto = proto; hdr->proto = proto;

File diff suppressed because it is too large Load Diff

View File

@ -1,206 +0,0 @@
/* ****************************************************************************
* PicoTCP. Copyright (c) 2014 TASS Belgium NV. Some rights reserved.
* See COPYING, LICENSE.GPLv2 and LICENSE.GPLv3 for usage.
* .
* Author: Toon Stegen, Jelle De Vleeschouwer
* ****************************************************************************/
#ifndef INCLUDE_PICO_MDNS
#define INCLUDE_PICO_MDNS
#include "pico_dns_common.h"
#include "pico_tree.h"
#include "pico_ipv4.h"
/* ********************************* CONFIG ***********************************/
#define PICO_MDNS_PROBE_UNICAST 1 /* Probe queries as QU-questions */
#define PICO_MDNS_CONTINUOUS_REFRESH 0 /* Continuously update cache */
#define PICO_MDNS_ALLOW_CACHING 1 /* Enable caching on this host */
#define PICO_MDNS_DEFAULT_TTL 120 /* Default TTL of mDNS records */
#define PICO_MDNS_SERVICE_TTL 120 /* Default TTL of SRV/TXT/PTR/NSEC */
#define PICO_MDNS_PROBE_COUNT 3
/* Amount of probes to send:
RFC6762: 8.1. Probing:
250 ms after the first query, the host should send a second; then,
250 ms after that, a third. If, by 250 ms after the third probe, no
conflicting Multicast DNS responses have been received, the host may
move to the next step, announcing.
*/
#define PICO_MDNS_ANNOUNCEMENT_COUNT 3
/* Amount of announcements to send: (we've opted for 1 extra for robustness)
RFC6762: 8.3. Announcing:
The Multicast DNS responder MUST send at least two unsolicited
responses, one second apart. To provide increased robustness against
packet loss, a responder MAY send up to eight unsolicited responses,
provided that the interval between unsolicited responses increases by
at least a factor of two with every response sent.
*/
/* ****************************************************************************/
#define PICO_MDNS_DEST_ADDR4 "224.0.0.251"
/* To make mDNS records unique or shared records */
#define PICO_MDNS_RECORD_UNIQUE 0x00u
#define PICO_MDNS_RECORD_SHARED 0x01u
/* To indicate if we reclaim or not */
#define PICO_MDNS_RECLAIM 1
#define PICO_MDNS_NO_RECLAIM 0
/* Flag to check for when records are returned, to determine the hostname */
#define PICO_MDNS_RECORD_HOSTNAME 0x02u
#define IS_HOSTNAME_RECORD(x) \
(((x)->flags) & PICO_MDNS_RECORD_HOSTNAME) ? (1) : (0)
/* --- MDNS resource record --- */
struct pico_mdns_record
{
struct pico_dns_record *record; /* DNS Resource Record */
uint32_t current_ttl; /* Current TTL */
uint8_t flags; /* Resource Record flags */
uint8_t claim_id; /* Claim ID number */
};
/* ****************************************************************************
* Compares 2 mDNS records by type, name AND rdata for a truly unique result
*
* @param ra mDNS record A
* @param rb mDNS record B
* @return 0 when records are equal, returns difference when they're not.
* ****************************************************************************/
int
pico_mdns_record_cmp( void *a, void *b );
/* ****************************************************************************
* Deletes a single mDNS resource record.
*
* @param record Void-pointer to mDNS Resource Record. Can be used with pico_-
* tree-destroy.
* @return Returns 0 on success, something else on failure.
* ****************************************************************************/
int
pico_mdns_record_delete( void **record );
/* ****************************************************************************
* Creates a single standalone mDNS resource record with given name, type and
* data to register on the network.
*
* @param url DNS rrecord name in URL format. Will be converted to DNS
* name notation format.
* @param _rdata Memory buffer with data to insert in the resource record. If
* data of record should contain a DNS name, the name in the
* databuffer needs to be in URL-format.
* @param datalen The exact length in bytes of the _rdata-buffer. If data of
* record should contain a DNS name, datalen needs to be
* pico_dns_strlen(_rdata).
* @param rtype DNS type of the resource record to be.
* @param rclass DNS class of the resource record to be.
* @param rttl DNS ttl of the resource record to be.
* @param flags You can specify if the mDNS record should be a shared record
* rather than a unique record.
* @return Pointer to newly created mDNS resource record.
* ****************************************************************************/
struct pico_mdns_record *
pico_mdns_record_create( const char *url,
void *_rdata,
uint16_t datalen,
uint16_t rtype,
uint32_t rttl,
uint8_t flags );
/* ****************************************************************************
* Definition of DNS record tree
* ****************************************************************************/
typedef struct pico_tree pico_mdns_rtree;
#define PICO_MDNS_RTREE_DECLARE(name) \
pico_mdns_rtree (name) = {&LEAF, pico_mdns_record_cmp}
#define PICO_MDNS_RTREE_DESTROY(rtree) \
pico_tree_destroy((rtree), pico_mdns_record_delete)
#define PICO_MDNS_RTREE_ADD(tree, record) \
pico_tree_insert((tree), (record))
/* ****************************************************************************
* API-call to query a record with a certain URL and type. First checks the
* Cache for this record. If no cache-entry is found, a query will be sent on
* the wire for this record.
*
* @param url URL to query for.
* @param type DNS type top query for.
* @param callback Callback to call when records are found for the query.
* @return 0 when query is correctly parsed, something else on failure.
* ****************************************************************************/
int
pico_mdns_getrecord( const char *url, uint16_t type,
void (*callback)(pico_mdns_rtree *,
char *,
void *),
void *arg );
/* ****************************************************************************
* Claim all different mDNS records in a tree in a single API-call. All records
* in tree are called in a single new claim-session.
*
* @param rtree mDNS record tree with records to claim
* @param callback Callback to call when all record are properly claimed.
* @return 0 When claiming didn't horribly fail.
* ****************************************************************************/
int
pico_mdns_claim( pico_mdns_rtree record_tree,
void (*callback)(pico_mdns_rtree *,
char *,
void *),
void *arg );
/* ****************************************************************************
* Tries to claim a hostname for this machine. Claims automatically a
* unique A record with the IPv4-address of this host.
* The hostname won't be set directly when this functions returns,
* but only if the claiming of the unique record succeeded.
* Init-callback will be called when the hostname-record is successfully
* registered.
*
* @param url URL to set the hostname to.
* @param arg Argument to pass to the init-callback.
* @return 0 when the host started registering the hostname-record successfully,
* Returns something else when it didn't succeeded.
* ****************************************************************************/
int
pico_mdns_tryclaim_hostname( const char *url, void *arg );
/* ****************************************************************************
* Get the current hostname for this machine.
*
* @return Returns the hostname for this machine when the module is initialised
* Returns NULL when the module is not initialised.
* ****************************************************************************/
const char *
pico_mdns_get_hostname( void );
/* ****************************************************************************
* Initialises the entire mDNS-module and sets the hostname for this machine.
* Sets up the global mDNS socket properly and calls callback when succeeded.
* Only when the module is properly initialised records can be registered on
* the module.
*
* @param hostname URL to set the hostname to.
* @param address IPv4-address of this host to bind to.
* @param callback Callback to call when the hostname is registered and
* also the global mDNS module callback. Gets called when
* Passive conflicts occur, so changes in records can be
* tracked in this callback.
* @param arg Argument to pass to the init-callback.
* @return 0 when the module is properly initialised and the host started regis-
* tering the hostname. Returns something else went the host failed
* initialising the module or registering the hostname.
* ****************************************************************************/
int
pico_mdns_init( const char *hostname,
struct pico_ip4 address,
void (*callback)(pico_mdns_rtree *,
char *,
void *),
void *arg );
#endif /* _INCLUDE_PICO_MDNS */

View File

@ -980,6 +980,8 @@ static int tcp_send(struct pico_socket_tcp *ts, struct pico_frame *f)
hdr->crc = 0; hdr->crc = 0;
hdr->crc = short_be(pico_tcp_checksum(f)); hdr->crc = short_be(pico_tcp_checksum(f));
f->local_ip.addr = ts->sock.local_addr.ip4.addr; // Masqueraded
return tcp_send_try_enqueue(ts, f); return tcp_send_try_enqueue(ts, f);
} }
@ -1318,6 +1320,7 @@ static void tcp_send_empty(struct pico_socket_tcp *t, uint16_t flags, int is_kee
hdr->crc = short_be(pico_tcp_checksum(f)); hdr->crc = short_be(pico_tcp_checksum(f));
/* TCP: ENQUEUE to PROTO */ /* TCP: ENQUEUE to PROTO */
f->local_ip.addr = t->sock.local_addr.ip4.addr; // Masqueraded
pico_enqueue(&tcp_out, f); pico_enqueue(&tcp_out, f);
} }
@ -1363,6 +1366,7 @@ static int tcp_do_send_rst(struct pico_socket *s, uint32_t seq)
hdr->crc = short_be(pico_tcp_checksum(f)); hdr->crc = short_be(pico_tcp_checksum(f));
/* TCP: ENQUEUE to PROTO */ /* TCP: ENQUEUE to PROTO */
f->local_ip.addr = s->local_addr.ip4.addr; // Masqueraded
pico_enqueue(&tcp_out, f); pico_enqueue(&tcp_out, f);
tcp_dbg("TCP SEND_RST >>>>>>>>>>>>>>> DONE\n"); tcp_dbg("TCP SEND_RST >>>>>>>>>>>>>>> DONE\n");
return 0; return 0;
@ -1535,6 +1539,7 @@ static int tcp_nosync_rst(struct pico_socket *s, struct pico_frame *fr)
hdr->crc = short_be(pico_tcp_checksum(f)); hdr->crc = short_be(pico_tcp_checksum(f));
/* TCP: ENQUEUE to PROTO */ /* TCP: ENQUEUE to PROTO */
f->local_ip.addr = s->local_addr.ip4.addr; // Masqueraded
pico_enqueue(&tcp_out, f); pico_enqueue(&tcp_out, f);
/***************************************************************************/ /***************************************************************************/
@ -1585,6 +1590,7 @@ static void tcp_send_fin(struct pico_socket_tcp *t)
hdr->crc = short_be(pico_tcp_checksum(f)); hdr->crc = short_be(pico_tcp_checksum(f));
/* tcp_dbg("SENDING FIN...\n"); */ /* tcp_dbg("SENDING FIN...\n"); */
if (t->linger_timeout > 0) { if (t->linger_timeout > 0) {
f->local_ip.addr = t->sock.local_addr.ip4.addr; // Masqueraded
pico_enqueue(&tcp_out, f); pico_enqueue(&tcp_out, f);
t->snd_nxt++; t->snd_nxt++;
} else { } else {
@ -1853,6 +1859,7 @@ static int tcp_rto_xmit(struct pico_socket_tcp *t, struct pico_frame *f)
return -1; return -1;
} }
cpy->local_ip.addr = t->sock.local_addr.ip4.addr; // Masqueraded
if (pico_enqueue(&tcp_out, cpy) > 0) { if (pico_enqueue(&tcp_out, cpy) > 0) {
t->snd_last_out = SEQN(cpy); t->snd_last_out = SEQN(cpy);
add_retransmission_timer(t, (t->rto << (++t->backoff)) + TCP_TIME); add_retransmission_timer(t, (t->rto << (++t->backoff)) + TCP_TIME);
@ -2010,6 +2017,7 @@ static int tcp_retrans(struct pico_socket_tcp *t, struct pico_frame *f)
return -1; return -1;
} }
cpy->local_ip.addr = t->sock.local_addr.ip4.addr; // Masqueraded
if (pico_enqueue(&tcp_out, cpy) > 0) { if (pico_enqueue(&tcp_out, cpy) > 0) {
t->in_flight++; t->in_flight++;
t->snd_last_out = SEQN(cpy); t->snd_last_out = SEQN(cpy);
@ -2386,6 +2394,9 @@ static int tcp_syn(struct pico_socket *s, struct pico_frame *f)
if (!new) if (!new)
return -1; return -1;
if (s->local_port == 0)
new->sock.local_port = hdr->trans.dport; // Masqueraded
#ifdef PICO_TCP_SUPPORT_SOCKET_STATS #ifdef PICO_TCP_SUPPORT_SOCKET_STATS
if (!pico_timer_add(2000, sock_stats, s)) { if (!pico_timer_add(2000, sock_stats, s)) {
tcp_dbg("TCP: Failed to start socket statistics timer\n"); tcp_dbg("TCP: Failed to start socket statistics timer\n");
@ -3130,7 +3141,10 @@ int pico_tcp_push(struct pico_protocol *self, struct pico_frame *f)
struct pico_socket_tcp *t = (struct pico_socket_tcp *) f->sock; struct pico_socket_tcp *t = (struct pico_socket_tcp *) f->sock;
IGNORE_PARAMETER(self); IGNORE_PARAMETER(self);
pico_err = PICO_ERR_NOERR; pico_err = PICO_ERR_NOERR;
hdr->trans.sport = t->sock.local_port; if (f->local_port)
hdr->trans.sport = f->local_port; // Masqueraded
else
hdr->trans.sport = t->sock.local_port;
hdr->trans.dport = t->sock.remote_port; hdr->trans.dport = t->sock.remote_port;
hdr->seq = long_be(t->snd_last + 1); hdr->seq = long_be(t->snd_last + 1);
hdr->len = (uint8_t)((f->payload - f->transport_hdr) << 2u | (int8_t)t->jumbo); hdr->len = (uint8_t)((f->payload - f->transport_hdr) << 2u | (int8_t)t->jumbo);
@ -3276,6 +3290,13 @@ int pico_tcp_get_bufsize_in(struct pico_socket *s, uint32_t *value)
return 0; return 0;
} }
int pico_tcp_get_bufspace_out(struct pico_socket *s, uint32_t *value)
{
struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;
*value = t->tcpq_out.max_size - t->tcpq_out.size;
return 0;
}
int pico_tcp_get_bufsize_out(struct pico_socket *s, uint32_t *value) int pico_tcp_get_bufsize_out(struct pico_socket *s, uint32_t *value)
{ {
struct pico_socket_tcp *t = (struct pico_socket_tcp *)s; struct pico_socket_tcp *t = (struct pico_socket_tcp *)s;

View File

@ -95,6 +95,7 @@ void pico_tcp_flags_update(struct pico_frame *f, struct pico_socket *s);
int pico_tcp_set_bufsize_in(struct pico_socket *s, uint32_t value); int pico_tcp_set_bufsize_in(struct pico_socket *s, uint32_t value);
int pico_tcp_set_bufsize_out(struct pico_socket *s, uint32_t value); int pico_tcp_set_bufsize_out(struct pico_socket *s, uint32_t value);
int pico_tcp_get_bufsize_in(struct pico_socket *s, uint32_t *value); int pico_tcp_get_bufsize_in(struct pico_socket *s, uint32_t *value);
int pico_tcp_get_bufspace_out(struct pico_socket *s, uint32_t *value);
int pico_tcp_get_bufsize_out(struct pico_socket *s, uint32_t *value); int pico_tcp_get_bufsize_out(struct pico_socket *s, uint32_t *value);
int pico_tcp_set_keepalive_probes(struct pico_socket *s, uint32_t value); int pico_tcp_set_keepalive_probes(struct pico_socket *s, uint32_t value);
int pico_tcp_set_keepalive_intvl(struct pico_socket *s, uint32_t value); int pico_tcp_set_keepalive_intvl(struct pico_socket *s, uint32_t value);

View File

@ -107,7 +107,10 @@ static int pico_udp_push(struct pico_protocol *self, struct pico_frame *f)
/* this (fragmented) frame should contain a transport header */ /* this (fragmented) frame should contain a transport header */
if (f->transport_hdr != f->payload) { if (f->transport_hdr != f->payload) {
hdr->trans.sport = f->sock->local_port; if (f->local_port)
hdr->trans.sport = f->local_port; // Masqueraded
else
hdr->trans.sport = f->sock->local_port;
if (remote_endpoint) { if (remote_endpoint) {
hdr->trans.dport = remote_endpoint->remote_port; hdr->trans.dport = remote_endpoint->remote_port;
} else { } else {
@ -173,6 +176,8 @@ static void pico_udp_get_msginfo(struct pico_frame *f, struct pico_msginfo *msgi
struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *)(f->net_hdr); struct pico_ipv4_hdr *hdr = (struct pico_ipv4_hdr *)(f->net_hdr);
msginfo->ttl = hdr->ttl; msginfo->ttl = hdr->ttl;
msginfo->tos = hdr->tos; msginfo->tos = hdr->tos;
msginfo->local_addr.ip4 = hdr->dst;
msginfo->local_port = ((struct pico_udp_hdr *)f->transport_hdr)->trans.dport;
#endif #endif
} else { } else {
#ifdef PICO_SUPPORT_IPV6 #ifdef PICO_SUPPORT_IPV6

View File

@ -278,24 +278,21 @@ struct pico_frame *pico_frame_deepcopy(struct pico_frame *f)
static inline uint32_t pico_checksum_adder(uint32_t sum, void *data, uint32_t len) static inline uint32_t pico_checksum_adder(uint32_t sum, void *data, uint32_t len)
{ {
uint16_t *buf = (uint16_t *)data; uint8_t *p = (uint8_t *)data;
uint16_t *stop; uint8_t *stop = p + len;
while (p < stop)
if (len & 0x01) { {
--len;
#ifdef PICO_BIGENDIAN #ifdef PICO_BIGENDIAN
sum += (((uint8_t *)data)[len]) << 8; sum += *p++ << 8;
if (p < stop)
sum += *p++;
#else #else
sum += ((uint8_t *)data)[len]; sum += *p++;
if (p < stop)
sum += *p++ << 8;
#endif #endif
} }
return sum;
stop = (uint16_t *)(((uint8_t *)data) + len);
while (buf < stop) {
sum += *buf++;
}
return sum;
} }
static inline uint16_t pico_checksum_finalize(uint32_t sum) static inline uint16_t pico_checksum_finalize(uint32_t sum)

View File

@ -582,11 +582,19 @@ static int pico_socket_deliver(struct pico_protocol *p, struct pico_frame *f, ui
if (!tr) if (!tr)
return -1; return -1;
// Try to find a socket using the local port number
sp = pico_get_sockport(p->proto_number, localport); sp = pico_get_sockport(p->proto_number, localport);
if (!sp) { if (sp)
dbg("No such port %d\n", short_be(localport)); {
return -1; if (pico_socket_transport_deliver(p, sp, f) == 0)
return 0;
} }
// Try to find a wildcard socket
sp = pico_get_sockport(p->proto_number, 0);
if (!sp) {
dbg("No such port %d\n", short_be(localport));
return -1;
}
return pico_socket_transport_deliver(p, sp, f); return pico_socket_transport_deliver(p, sp, f);
} }
@ -1118,6 +1126,8 @@ static int pico_socket_xmit_one(struct pico_socket *s, const void *buf, const in
if (msginfo) { if (msginfo) {
f->send_ttl = (uint8_t)msginfo->ttl; f->send_ttl = (uint8_t)msginfo->ttl;
f->send_tos = (uint8_t)msginfo->tos; f->send_tos = (uint8_t)msginfo->tos;
f->local_ip = msginfo->local_addr.ip4;
f->local_port = msginfo->local_port;
} }
memcpy(f->payload, (const uint8_t *)buf, f->payload_len); memcpy(f->payload, (const uint8_t *)buf, f->payload_len);
@ -1377,19 +1387,23 @@ int MOCKABLE pico_socket_sendto_extended(struct pico_socket *s, const void *buf,
if (pico_socket_sendto_initial_checks(s, buf, len, dst, remote_port) < 0) if (pico_socket_sendto_initial_checks(s, buf, len, dst, remote_port) < 0)
return -1; return -1;
if (msginfo && msginfo->local_addr.ip4.addr)
src = pico_socket_sendto_get_src(s, dst); src = &msginfo->local_addr.ip4;
if (!src) { else
{
src = pico_socket_sendto_get_src(s, dst);
if (!src) {
#ifdef PICO_SUPPORT_IPV6 #ifdef PICO_SUPPORT_IPV6
if((s->net->proto_number == PICO_PROTO_IPV6) if((s->net->proto_number == PICO_PROTO_IPV6)
&& msginfo && msginfo->dev && msginfo && msginfo->dev
&& pico_ipv6_is_multicast(((struct pico_ip6 *)dst)->addr)) && pico_ipv6_is_multicast(((struct pico_ip6 *)dst)->addr))
{ {
src = &(pico_ipv6_linklocal_get(msginfo->dev)->address); src = &(pico_ipv6_linklocal_get(msginfo->dev)->address);
} }
else else
#endif #endif
return -1; return -1;
}
} }
remote_endpoint = pico_socket_sendto_destination(s, dst, remote_port); remote_endpoint = pico_socket_sendto_destination(s, dst, remote_port);
@ -1589,13 +1603,14 @@ int MOCKABLE pico_socket_bind(struct pico_socket *s, void *local_addr, uint16_t
} }
/* When given port = 0, get a random high port to bind to. */ /* When given port = 0, get a random high port to bind to. */
if (*port == 0) { // Port 0 is used as a wildcard for "all ports"
*port = pico_socket_high_port(PROTO(s)); // if (*port == 0) {
if (*port == 0) { // *port = pico_socket_high_port(PROTO(s));
pico_err = PICO_ERR_EINVAL; // if (*port == 0) {
return -1; // pico_err = PICO_ERR_EINVAL;
} // return -1;
} // }
// }
if (pico_is_port_free(PROTO(s), *port, local_addr, s->net) == 0) { if (pico_is_port_free(PROTO(s), *port, local_addr, s->net) == 0) {
pico_err = PICO_ERR_EADDRINUSE; pico_err = PICO_ERR_EADDRINUSE;
@ -1757,33 +1772,73 @@ struct pico_socket *pico_socket_accept(struct pico_socket *s, void *orig, uint16
} }
if (TCPSTATE(s) == PICO_SOCKET_STATE_TCP_LISTEN) { if (TCPSTATE(s) == PICO_SOCKET_STATE_TCP_LISTEN) {
struct pico_sockport *sp = pico_get_sockport(PICO_PROTO_TCP, s->local_port); /* If at this point no incoming connection socket is found,
struct pico_socket *found; * the accept call is valid, but no connection is established yet.
uint32_t socklen = sizeof(struct pico_ip4); */
/* If at this point no incoming connection socket is found, struct pico_socket *found;
* the accept call is valid, but no connection is established yet. uint32_t socklen = sizeof(struct pico_ip4);
*/ struct pico_sockport *sp;
pico_err = PICO_ERR_EAGAIN;
if (sp) {
struct pico_tree_node *index;
/* RB_FOREACH(found, socket_tree, &sp->socks) { */
pico_tree_foreach(index, &sp->socks){
found = index->keyValue;
if ((s == found->parent) && ((found->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED)) {
found->parent = NULL;
pico_err = PICO_ERR_NOERR;
#ifdef PICO_SUPPORT_IPV6
if (is_sock_ipv6(s))
socklen = sizeof(struct pico_ip6);
#endif pico_err = PICO_ERR_EAGAIN;
memcpy(orig, &found->remote_addr, socklen);
*port = found->remote_port; if (s->local_port == 0)
s->number_of_pending_conn--; {
return found; // Wildcard socket: do a full scan of the TCP table to find a new child
} struct pico_tree_node *index, *indexp;
pico_tree_foreach(indexp, &TCPTable)
{
sp = indexp->keyValue;
if (sp)
{
pico_tree_foreach(index, &sp->socks)
{
found = index->keyValue;
if (found == NULL)
continue;
if ((s == found->parent) && ((found->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED)) {
found->parent = NULL;
pico_err = PICO_ERR_NOERR;
#ifdef PICO_SUPPORT_IPV6
if (is_sock_ipv6(s))
socklen = sizeof(struct pico_ip6);
#endif
memcpy(orig, &found->remote_addr, socklen);
*port = found->remote_port;
s->number_of_pending_conn--;
return found;
}
}
}
} }
} }
else
{
// Normal search using the local port
sp = pico_get_sockport(PICO_PROTO_TCP, s->local_port);
if (sp) {
struct pico_tree_node *index;
/* RB_FOREACH(found, socket_tree, &sp->socks) { */
pico_tree_foreach(index, &sp->socks){
found = index->keyValue;
if ((s == found->parent) && ((found->state & PICO_SOCKET_STATE_TCP) == PICO_SOCKET_STATE_TCP_ESTABLISHED)) {
found->parent = NULL;
pico_err = PICO_ERR_NOERR;
#ifdef PICO_SUPPORT_IPV6
if (is_sock_ipv6(s))
socklen = sizeof(struct pico_ip6);
#endif
memcpy(orig, &found->remote_addr, socklen);
*port = found->remote_port;
s->number_of_pending_conn--;
return found;
}
}
}
}
} }
return NULL; return NULL;

151
core/hw/modem/dns.cpp Normal file
View File

@ -0,0 +1,151 @@
/*
Created on: Sep 24, 2018
Copyright 2018 flyinghead
This file is part of reicast.
reicast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
reicast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with reicast. If not, see <https://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <errno.h>
#ifndef _WIN32
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#endif
extern "C" {
#include <pico_stack.h>
#include <pico_ipv4.h>
#include <pico_dns_common.h>
}
void get_host_by_name(const char *name, struct pico_ip4 dnsaddr);
int get_dns_answer(struct pico_ip4 *address, struct pico_ip4 dnsaddr);
char *read_name(char *reader, char *buffer, int *count);
void set_non_blocking(int fd);
static int sock_fd = -1;
static unsigned short qid = PICO_TIME_MS();
static int qname_len;
void get_host_by_name(const char *host, struct pico_ip4 dnsaddr)
{
if (sock_fd < 0)
{
sock_fd = socket(AF_INET , SOCK_DGRAM , IPPROTO_UDP);
set_non_blocking(sock_fd);
}
struct sockaddr_in dest;
dest.sin_family = AF_INET;
dest.sin_port = htons(53);
dest.sin_addr.s_addr = dnsaddr.addr;
// DNS Packet header
char buf[1024];
pico_dns_packet *dns = (pico_dns_packet *)&buf;
dns->id = qid++;
dns->qr = PICO_DNS_QR_QUERY;
dns->opcode = PICO_DNS_OPCODE_QUERY;
dns->aa = PICO_DNS_AA_NO_AUTHORITY;
dns->tc = PICO_DNS_TC_NO_TRUNCATION;
dns->rd = PICO_DNS_RD_IS_DESIRED;
dns->ra = PICO_DNS_RA_NO_SUPPORT;
dns->z = 0;
dns->rcode = PICO_DNS_RCODE_NO_ERROR;
dns->qdcount = htons(1); // One question
dns->ancount = 0;
dns->nscount = 0;
dns->arcount = 0;
char *qname = &buf[sizeof(pico_dns_packet)];
strcpy(qname + 1, host);
pico_dns_name_to_dns_notation(qname, 128);
qname_len = strlen(qname) + 1;
struct pico_dns_question_suffix *qinfo = (struct pico_dns_question_suffix *) &buf[sizeof(pico_dns_packet) + qname_len]; //fill it
qinfo->qtype = htons(PICO_DNS_TYPE_A); // Address record
qinfo->qclass = htons(PICO_DNS_CLASS_IN);
if (sendto(sock_fd, buf, sizeof(pico_dns_packet) + qname_len + sizeof(struct pico_dns_question_suffix), 0, (struct sockaddr *)&dest, sizeof(dest)) < 0)
perror("DNS sendto failed");
}
int get_dns_answer(struct pico_ip4 *address, struct pico_ip4 dnsaddr)
{
struct sockaddr_in peer;
socklen_t peer_len = sizeof(peer);
char buf[1024];
int r = recvfrom(sock_fd, buf, sizeof(buf), 0, (struct sockaddr*)&peer , &peer_len);
if (r < 0)
{
if (errno != EAGAIN && errno != EWOULDBLOCK)
perror("DNS recvfrom failed");
return -1;
}
if (peer.sin_addr.s_addr != dnsaddr.addr)
return -1;
pico_dns_packet *dns = (pico_dns_packet*) buf;
// move to the first answer
char *reader = &buf[sizeof(pico_dns_packet) + qname_len + sizeof(struct pico_dns_question_suffix)];
int stop = 0;
for (int i = 0; i < ntohs(dns->ancount); i++)
{
// FIXME Check name?
free(read_name(reader, buf, &stop));
reader = reader + stop;
struct pico_dns_record_suffix *record = (struct pico_dns_record_suffix *)reader;
reader = reader + sizeof(struct pico_dns_record_suffix);
if (ntohs(record->rtype) == PICO_DNS_TYPE_A) // Address record
{
memcpy(&address->addr, reader, 4);
return 0;
}
reader = reader + ntohs(record->rdlength);
}
return -1;
}
char *read_name(char *reader, char *buffer, int *count)
{
char *name = (char *)malloc(128);
if ((uint8_t)reader[0] & 0xC0)
{
int offset = (((uint8_t)reader[0] & ~0xC0) << 8) + (uint8_t)reader[1];
reader = &buffer[offset];
*count = 2;
}
else
{
*count = strlen(reader) + 1;
}
pico_dns_notation_to_name(reader, 128);
strcpy(name, reader + 1);
return name;
}

View File

@ -142,7 +142,9 @@ static int modem_sched_func(int tag, int c, int j)
{ {
if (last_comm_stats != 0) if (last_comm_stats != 0)
{ {
printf("Stats sent %d received %d TDBE %d RDBF %d\n", sent_bytes, recvd_bytes, modem_regs.reg1e.TDBE, modem_regs.reg1e.RDBF); printf("Stats sent %d (%.2f kB/s) received %d (%.2f kB/s) TDBE %d RDBF %d\n", sent_bytes, sent_bytes / 2000.0,
recvd_bytes, recvd_bytes / 2000.0,
modem_regs.reg1e.TDBE, modem_regs.reg1e.RDBF);
sent_bytes = 0; sent_bytes = 0;
recvd_bytes = 0; recvd_bytes = 0;
} }
@ -315,6 +317,7 @@ static int modem_sched_func(int tag, int c, int j)
static void schedule_callback(int ms) static void schedule_callback(int ms)
{ {
if (modem_sched == 0) if (modem_sched == 0)
// FIXME would break save state -> relies on all schedule to be registered at init
modem_sched = sh4_sched_register(0, &modem_sched_func); modem_sched = sh4_sched_register(0, &modem_sched_func);
sh4_sched_request(modem_sched, SH4_MAIN_CLOCK / 1000 * ms); sh4_sched_request(modem_sched, SH4_MAIN_CLOCK / 1000 * ms);
} }

View File

@ -1,51 +1,131 @@
/*
Created on: Sep 15, 2018
Copyright 2018 flyinghead
This file is part of reicast.
reicast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
reicast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with reicast. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef _MSC_VER #ifndef _MSC_VER
#include <queue> #include <queue>
#include <map>
extern "C" { extern "C" {
#include <pico_stack.h> #include <pico_stack.h>
#include <pico_dev_ppp.h> #include <pico_dev_ppp.h>
#ifdef _WIN32 #include <pico_socket.h>
#include <pico_dev_tap_windows.h> #include <pico_socket_tcp.h>
#else #include <pico_ipv4.h>
#include <pico_dev_tap.h> #include <pico_tcp.h>
#endif
#include <pico_arp.h>
#include <pico_dev_tun.h>
#ifdef DHCP
#include <pico_dhcp_client.h>
#endif
} }
#ifdef _WIN32 #ifndef _WIN32
#include <iphlpapi.h> #include <sys/types.h>
#define NETWORK_TAP #include <sys/socket.h>
#else #include <netinet/in.h>
#define NETWORK_TUN #include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#endif #endif
#include "types.h" #include "types.h"
#include "cfg/cfg.h" #include "cfg/cfg.h"
#include "picoppp.h" #include "picoppp.h"
#ifndef _WIN32
#define closesocket close
#endif
#define RESOLVER1_OPENDNS_COM "208.67.222.222"
#define AFO_ORIG_IP 0x83f2fb3f // 63.251.242.131 in network order
static struct pico_device *ppp; static struct pico_device *ppp;
struct pico_device* tap;
struct pico_device* tun;
u8 virtual_mac[] = { 0x76, 0x6D, 0x61, 0x63, 0x30, 0x31 };
static std::queue<u8> in_buffer; static std::queue<u8> in_buffer;
static std::queue<u8> out_buffer; static std::queue<u8> out_buffer;
static cMutex in_buffer_lock;
static cMutex out_buffer_lock;
struct pico_ip4 dcaddr;
struct pico_ip4 dnsaddr;
static struct pico_socket *pico_tcp_socket, *pico_udp_socket;
struct pico_ip4 public_ip;
struct pico_ip4 afo_ip;
// src socket -> socket fd
static map<struct pico_socket *, int> tcp_sockets;
// src port -> socket fd
static map<uint16_t, int> udp_sockets;
static const uint16_t games_udp_ports[] = {
7980, // Alien Front Online
9789, // ChuChu Rocket
// NBA/NFL/NCAA 2K Series
5502,
5503,
5656,
3512, // The Next Tetris
6001, // Ooga Booga
// PBA Tour Bowling 2001, Starlancer
// 2300-2400, ?
6500,
13139,
// Planet Ring
7648,
1285,
1028,
// World Series Baseball 2K2
37171,
13713,
};
static const uint16_t games_tcp_ports[] = {
// NBA/NFL/NCAA 2K Series
5011,
6666,
3512, // The Next Tetris
// PBA Tour Bowling 2001, Starlancer
// 2300-2400, ?
47624,
17219, // Worms World Party
};
// listening port -> socket fd
static map<uint16_t, int> tcp_listening_sockets;
static void read_native_sockets();
void get_host_by_name(const char *name, struct pico_ip4 dnsaddr);
int get_dns_answer(struct pico_ip4 *address, struct pico_ip4 dnsaddr);
static int modem_read(struct pico_device *dev, void *data, int len) static int modem_read(struct pico_device *dev, void *data, int len)
{ {
u8 *p = (u8 *)data; u8 *p = (u8 *)data;
int count = 0; int count = 0;
out_buffer_lock.Lock();
while (!out_buffer.empty() && count < len) while (!out_buffer.empty() && count < len)
{ {
*p++ = out_buffer.front(); *p++ = out_buffer.front();
out_buffer.pop(); out_buffer.pop();
count++; count++;
} }
out_buffer_lock.Unlock();
return count; return count;
} }
@ -54,73 +134,469 @@ static int modem_write(struct pico_device *dev, const void *data, int len)
{ {
u8 *p = (u8 *)data; u8 *p = (u8 *)data;
in_buffer_lock.Lock();
while (len > 0) while (len > 0)
{ {
in_buffer.push(*p++); in_buffer.push(*p++);
len--; len--;
} }
in_buffer_lock.Unlock();
return len; return len;
} }
void write_pico(u8 b) void write_pico(u8 b)
{ {
out_buffer_lock.Lock();
out_buffer.push(b); out_buffer.push(b);
out_buffer_lock.Unlock();
} }
int read_pico() int read_pico()
{ {
pico_stack_tick(); in_buffer_lock.Lock();
if (in_buffer.empty()) if (in_buffer.empty())
{
in_buffer_lock.Unlock();
return -1; return -1;
}
else else
{ {
u32 b = in_buffer.front(); u32 b = in_buffer.front();
in_buffer.pop(); in_buffer.pop();
in_buffer_lock.Unlock();
return b; return b;
} }
} }
void set_non_blocking(int fd)
{
#ifndef _WIN32
fcntl(fd, F_SETFL, O_NONBLOCK);
#else
uint32_t optl = 1;
ioctlsocket(fd, FIONBIO, &optl);
#endif
}
static void tcp_callback(uint16_t ev, struct pico_socket *s)
{
int r = 0;
if (ev & PICO_SOCK_EV_RD)
{
auto it = tcp_sockets.find(s);
if (it == tcp_sockets.end())
{
printf("Unknown socket: remote port %d\n", s->remote_port);
}
else
{
char buf[1510];
r = pico_socket_read(it->first, buf, sizeof(buf));
if (r > 0) {
if (write(it->second, buf, r) < r)
perror("tcp_callback write");
}
}
}
if (ev & PICO_SOCK_EV_CONN)
{
uint32_t ka_val = 0;
struct pico_ip4 orig;
uint16_t port;
char peer[30];
int yes = 1;
struct pico_socket *sock_a = pico_socket_accept(s, &orig, &port);
if (sock_a == NULL)
{
printf("pico_socket_accept: %s\n", strerror(pico_err));
}
else
{
pico_ipv4_to_string(peer, orig.addr);
printf("Connection established with %s:%d.\n", peer, short_be(port));
pico_socket_setoption(sock_a, PICO_TCP_NODELAY, &yes);
/* Set keepalive options */
// ka_val = 5;
// pico_socket_setoption(sock_a, PICO_SOCKET_OPT_KEEPCNT, &ka_val);
// ka_val = 30000;
// pico_socket_setoption(sock_a, PICO_SOCKET_OPT_KEEPIDLE, &ka_val);
// ka_val = 5000;
// pico_socket_setoption(sock_a, PICO_SOCKET_OPT_KEEPINTVL, &ka_val);
int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sockfd < 0)
{
perror("socket");
}
else
{
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = sock_a->local_addr.ip4.addr;
if (serveraddr.sin_addr.s_addr == AFO_ORIG_IP) // Alien Front Online
serveraddr.sin_addr.s_addr = afo_ip.addr;
serveraddr.sin_port = sock_a->local_port;
if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
{
pico_ipv4_to_string(peer, sock_a->local_addr.ip4.addr);
printf("TCP connection to %s:%d failed: ", peer, sock_a->local_port);
perror(NULL);
closesocket(sockfd);
}
else
{
set_non_blocking(sockfd);
int optval = 1;
socklen_t optlen = sizeof(optval);
#if !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__)
setsockopt(sockfd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen);
#else
struct protoent *tcp_proto = getprotobyname("TCP");
setsockopt(sockfd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen);
#endif
tcp_sockets[sock_a] = sockfd;
}
}
}
}
if (ev & PICO_SOCK_EV_FIN) {
//printf("Socket closed. Exit normally. \n");
auto it = tcp_sockets.find(s);
if (it == tcp_sockets.end())
{
printf("PICO_SOCK_EV_FIN: Unknown socket: remote port %d\n", s->remote_port);
}
else
{
closesocket(it->second);
pico_socket_close(s);
tcp_sockets.erase(s);
}
}
if (ev & PICO_SOCK_EV_ERR) {
printf("Socket error received: %s. Bailing out.\n", strerror(pico_err));
auto it = tcp_sockets.find(s);
if (it == tcp_sockets.end())
{
printf("PICO_SOCK_EV_ERR: Unknown socket: remote port %d\n", s->remote_port);
}
else
{
closesocket(it->second);
pico_socket_close(s);
tcp_sockets.erase(s);
}
}
// if (ev & PICO_SOCK_EV_CLOSE)
// {
// }
// if (ev & PICO_SOCK_EV_WR)
// {
// }
}
static int find_udp_socket(uint16_t src_port)
{
auto it = udp_sockets.find(src_port);
if (it != udp_sockets.end())
return it->second;
int sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sockfd < 0)
{
perror("socket");
return -1;
}
#ifndef _WIN32
fcntl(sockfd, F_SETFL, O_NONBLOCK);
#else
uint32_t optl = 1;
ioctlsocket(sockfd, FIONBIO, &optl);
#endif
// FIXME Need to clean up at some point?
udp_sockets[src_port] = sockfd;
return sockfd;
}
static void udp_callback(uint16_t ev, struct pico_socket *s)
{
if (ev & PICO_SOCK_EV_RD)
{
char buf[1510];
struct pico_ip4 src_addr;
uint16_t src_port;
pico_msginfo msginfo;
int r = 0;
while (true)
{
r = pico_socket_recvfrom_extended(s, buf, sizeof(buf), &src_addr.addr, &src_port, &msginfo);
if (r <= 0)
{
if (r < 0)
printf("%s: error UDP recv: %s\n", __FUNCTION__, strerror(pico_err));
break;
}
int sockfd = find_udp_socket(src_port);
if (sockfd >= 0)
{
struct sockaddr_in dst_addr;
socklen_t addr_len = sizeof(dst_addr);
memset(&dst_addr, 0, sizeof(dst_addr));
dst_addr.sin_family = AF_INET;
dst_addr.sin_addr.s_addr = msginfo.local_addr.ip4.addr;
dst_addr.sin_port = msginfo.local_port;
if (sendto(sockfd, buf, r, 0, (const struct sockaddr *)&dst_addr, addr_len) < 0)
perror("sendto udp socket");
}
}
}
if (ev & PICO_SOCK_EV_ERR) {
printf("UDP Callback error received\n");
}
}
static void read_native_sockets()
{
int r;
struct sockaddr_in src_addr;
socklen_t addr_len;
// Accept incoming TCP connections
for (auto it = tcp_listening_sockets.begin(); it != tcp_listening_sockets.end(); it++)
{
addr_len = sizeof(src_addr);
memset(&src_addr, 0, addr_len);
int sockfd = accept(it->second, (struct sockaddr *)&src_addr, &addr_len);
if (sockfd < 0)
{
if (errno != EAGAIN && errno != EWOULDBLOCK)
perror("accept");
continue;
}
printf("Incoming TCP connection from %08x to port %d\n", src_addr.sin_addr.s_addr, short_be(it->first));
struct pico_socket *ps = pico_socket_tcp_open(PICO_PROTO_IPV4);
if (ps == NULL)
{
printf("pico_socket_tcp_open failed: error %d\n", pico_err);
closesocket(sockfd);
continue;
}
ps->local_addr.ip4.addr = src_addr.sin_addr.s_addr;
ps->local_port = src_addr.sin_port;
if (pico_socket_connect(ps, &dcaddr.addr, it->first) != 0)
{
printf("pico_socket_connect failed: error %d\n", pico_err);
closesocket(sockfd);
pico_socket_del(ps);
continue;
}
tcp_sockets[ps] = sockfd;
}
char buf[1500]; // FIXME MTU ?
struct pico_msginfo msginfo;
// If modem buffer is full, wait
in_buffer_lock.Lock();
size_t in_buffer_size = in_buffer.size();
in_buffer_lock.Unlock();
if (in_buffer_size >= 256)
return;
// Read UDP sockets
for (auto it = udp_sockets.begin(); it != udp_sockets.end(); it++)
{
addr_len = sizeof(src_addr);
memset(&src_addr, 0, addr_len);
r = recvfrom(it->second, buf, sizeof(buf), 0, (struct sockaddr *)&src_addr, &addr_len);
if (r > 0)
{
msginfo.dev = ppp;
msginfo.tos = 0;
msginfo.ttl = 0;
msginfo.local_addr.ip4.addr = src_addr.sin_addr.s_addr;
msginfo.local_port = src_addr.sin_port;
// printf("read_native_sockets UCP received %d bytes from %08x:%d\n", r, long_be(msginfo.local_addr.ip4.addr), short_be(msginfo.local_port));
int r2 = pico_socket_sendto_extended(pico_udp_socket, buf, r, &dcaddr, it->first, &msginfo);
if (r2 < r)
printf("%s: error UDP sending to %d: %s\n", __FUNCTION__, short_be(it->first), strerror(pico_err));
}
else if (r < 0 && errno != EAGAIN)
{
perror("read udp socket");
continue;
}
}
// Read TCP sockets
for (auto it = tcp_sockets.begin(); it != tcp_sockets.end(); it++)
{
uint32_t space;
pico_tcp_get_bufspace_out(it->first, &space);
if (space < sizeof(buf))
{
// Wait for the out buffer to empty a bit
continue;
}
r = read(it->second, buf, sizeof(buf));
if (r > 0)
{
// printf("read_native_sockets TCP received %d bytes\n", r);
int r2 = pico_socket_send(it->first, buf, r);
if (r2 < 0)
printf("%s: error TCP sending: %s\n", __FUNCTION__, strerror(pico_err));
else if (r2 < r)
// FIXME EAGAIN errors. Need to buffer data or wait for call back.
printf("%s: truncated send: %d -> %d\n", __FUNCTION__, r, r2);
}
else if (r < 0 && errno != EAGAIN)
{
perror("read tcp socket");
closesocket(it->second);
pico_socket_close(it->first);
tcp_sockets.erase(it);
continue;
}
}
}
void close_native_sockets()
{
for (auto it = udp_sockets.begin(); it != udp_sockets.end(); it++)
closesocket(it->second);
udp_sockets.clear();
for (auto it = tcp_sockets.begin(); it != tcp_sockets.end(); it++)
{
pico_socket_close(it->first);
closesocket(it->second);
}
tcp_sockets.clear();
}
static int modem_set_speed(struct pico_device *dev, uint32_t speed) static int modem_set_speed(struct pico_device *dev, uint32_t speed)
{ {
return 0; return 0;
} }
#ifdef DHCP #ifdef _WIN32
static uint32_t dhcpclient_xid; static void usleep(unsigned int usec)
static struct pico_ip4 dchp_address = { 0 };
void callback_dhcpclient(void *arg, int code)
{ {
char s_address[16] = { }, s_gateway[16] = { }; HANDLE timer;
LARGE_INTEGER ft;
printf("DHCP client: callback happened with code %d!\n", code); ft.QuadPart = -(10 * (__int64)usec);
if (code == PICO_DHCP_SUCCESS)
{ timer = CreateWaitableTimer(NULL, TRUE, NULL);
dchp_address = pico_dhcp_get_address(arg); SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
//gateway = pico_dhcp_get_gateway(arg); WaitForSingleObject(timer, INFINITE);
pico_ipv4_to_string(s_address, dchp_address.addr); CloseHandle(timer);
//pico_ipv4_to_string(s_gateway, gateway.addr);
printf("DHCP client: got IP %s assigned with cli %p\n", s_address, arg);
}
} }
#endif #endif
static bool pico_stack_inited; static void check_dns_entries()
bool start_pico()
{ {
struct pico_ip4 ipaddr, dcaddr, dnsaddr, netmask, zero = { static uint32_t dns_query_start = 0;
static uint32_t dns_query_attempts = 0;
if (public_ip.addr == 0)
{
if (!dns_query_start)
{
dns_query_start = PICO_TIME_MS();
struct pico_ip4 tmpdns;
pico_string_to_ipv4(RESOLVER1_OPENDNS_COM, &tmpdns.addr);
get_host_by_name("myip.opendns.com", tmpdns);
}
else
{
struct pico_ip4 tmpdns;
pico_string_to_ipv4(RESOLVER1_OPENDNS_COM, &tmpdns.addr);
if (get_dns_answer(&public_ip, tmpdns) == 0)
{
dns_query_attempts = 0;
dns_query_start = 0;
char myip[16];
pico_ipv4_to_string(myip, public_ip.addr);
printf("My IP is %s\n", myip);
}
else
{
if (PICO_TIME_MS() - dns_query_start > 1000)
{
if (++dns_query_attempts >= 5)
{
public_ip.addr = 0xffffffff; // Bogus but not null
dns_query_attempts = 0;
}
else
// Retry
dns_query_start = 0;
}
}
}
}
else if (afo_ip.addr == 0)
{
if (!dns_query_start)
{
dns_query_start = PICO_TIME_MS();
get_host_by_name("auriga.segasoft.com", dnsaddr); // Alien Front Online server
}
else
{
if (get_dns_answer(&afo_ip, dnsaddr) == 0)
{
dns_query_attempts = 0;
dns_query_start = 0;
char afoip[16];
pico_ipv4_to_string(afoip, afo_ip.addr);
printf("AFO server IP is %s\n", afoip);
}
else
{
if (PICO_TIME_MS() - dns_query_start > 1000)
{
if (++dns_query_attempts >= 5)
{
pico_string_to_ipv4("146.185.135.179", &afo_ip.addr); // Default address
dns_query_attempts = 0;
}
else
// Retry
dns_query_start = 0;
}
}
}
}
}
static bool pico_stack_inited;
static bool pico_thread_running = false;
static void *pico_thread_func(void *)
{
struct pico_ip4 ipaddr, netmask, zero = {
0 0
}; };
#ifdef _WIN32
// No de-init on Windows yet
if (pico_stack_inited)
return;
#endif
if (!pico_stack_inited) if (!pico_stack_inited)
{ {
pico_stack_init(); pico_stack_init();
@ -130,210 +606,121 @@ bool start_pico()
// PPP // PPP
ppp = pico_ppp_create(); ppp = pico_ppp_create();
if (!ppp) if (!ppp)
return false; return NULL;
string dc_ip = cfgLoadStr("network", "IP", ""); pico_string_to_ipv4("192.168.167.2", &dcaddr.addr);
if (dc_ip.length() == 0)
{
printf("No IP address set for Netplay. Set IP= in the [network] section\n");
return false;
}
pico_string_to_ipv4(dc_ip.c_str(), &dcaddr.addr);
pico_ppp_set_peer_ip(ppp, dcaddr); pico_ppp_set_peer_ip(ppp, dcaddr);
pico_string_to_ipv4("192.168.167.1", &ipaddr.addr); pico_string_to_ipv4("192.168.167.1", &ipaddr.addr);
pico_ppp_set_ip(ppp, ipaddr); pico_ppp_set_ip(ppp, ipaddr);
string dns_ip = cfgLoadStr("network", "DNS", "46.101.91.123"); string dns_ip = cfgLoadStr("network", "DNS", "46.101.91.123"); // Dreamcast Live DNS
pico_string_to_ipv4(dns_ip.c_str(), &dnsaddr.addr); pico_string_to_ipv4(dns_ip.c_str(), &dnsaddr.addr);
pico_ppp_set_dns1(ppp, dnsaddr); pico_ppp_set_dns1(ppp, dnsaddr);
#ifdef NETWORK_TAP pico_udp_socket = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_UDP, &udp_callback);
// TAP if (pico_udp_socket == NULL) {
// tap config: printf("%s: error opening UDP socket: %s\n", __FUNCTION__, strerror(pico_err));
// # ip tuntap add mode tap user joe
// # ifconfig tap0 192.168.5.5
// # ip link set tap0 up
// # ip route add <IP>/32 dev tap0 # where <IP> is the value of network:IP in emu.cfg./. This also allows proxy arp
// # echo '1' >/proc/sys/net/ipv4/conf/all/proxy_arp
// (or ...conf/tap0/proxy_arp and ...conf/eth0/proxy_arp only)
#ifdef _WIN32
tap = pico_tap_create("tap0", virtual_mac);
#else
tap = pico_tap_create("tap0");
#endif
if (!tap)
{
stop_pico();
return false;
} }
int yes = 1;
struct pico_ip4 inaddr_any = {0};
uint16_t listen_port = 0;
int ret = pico_socket_bind(pico_udp_socket, &inaddr_any, &listen_port);
if (ret < 0)
printf("%s: error binding UDP socket to port %u: %s\n", __FUNCTION__, short_be(listen_port), strerror(pico_err));
pico_string_to_ipv4("192.168.166.2", &ipaddr.addr); pico_tcp_socket = pico_socket_open(PICO_PROTO_IPV4, PICO_PROTO_TCP, &tcp_callback);
pico_string_to_ipv4("255.255.255.0", &netmask.addr); if (pico_tcp_socket == NULL) {
pico_ipv4_link_add(tap, ipaddr, netmask); printf("%s: error opening TCP socket: %s\n", __FUNCTION__, strerror(pico_err));
// Proxy ARP }
pico_arp_create_entry(tap->eth->mac.addr, ipaddr, ppp); pico_socket_setoption(pico_tcp_socket, PICO_TCP_NODELAY, &yes);
ret = pico_socket_bind(pico_tcp_socket, &inaddr_any, &listen_port);
#ifdef _WIN32 if (ret < 0) {
int err; printf("%s: error binding TCP socket to port %u: %s\n", __FUNCTION__, short_be(listen_port), strerror(pico_err));
}
// Enable routing
OVERLAPPED overlapped;
memset(&overlapped, 0, sizeof(overlapped));
overlapped.hEvent = overlapped.hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
if (overlapped.hEvent == NULL)
printf("CreateEvent failed with error %d\n", GetLastError());
else else
{ {
HANDLE handle; if (pico_socket_listen(pico_tcp_socket, 10) != 0)
err = EnableRouter(&handle, &overlapped); printf("%s: error listening on port %u\n", __FUNCTION__, short_be(listen_port));
if (err != ERROR_IO_PENDING)
printf("EnableRouter failed with error %d\n", err);
else
printf("Windows router enabled\n");
CloseHandle(overlapped.hEvent);
} }
ppp->proxied = 1;
// Get the LAN interface index struct sockaddr_in saddr;
DWORD idx = -1; socklen_t saddr_len = sizeof(saddr);
err = GetBestInterface(dnsaddr.addr, &idx); memset(&saddr, 0, sizeof(saddr));
if (err != NO_ERROR) saddr.sin_family = AF_INET;
printf("GetBestInterface failed error %d\n", err); saddr.sin_addr.s_addr = INADDR_ANY;
for (int i = 0; i < sizeof(games_udp_ports) / sizeof(uint16_t); i++)
// Create a Proxy ARP entry for the DC on the local LAN
if (idx != -1)
{ {
err = CreateProxyArpEntry(dcaddr.addr, 0xffffffff, idx); uint16_t port = short_be(games_udp_ports[i]);
if (err == ERROR_OBJECT_ALREADY_EXISTS) int sockfd = find_udp_socket(port);
printf("Proxy ARP entry already exists\n"); saddr.sin_port = port;
else if (err != NO_ERROR)
printf("CreateProxyArpEntry failed error %d\n", err); bind(sockfd, (struct sockaddr *)&saddr, saddr_len);
} }
// Get the TAP interface index for (int i = 0; i < sizeof(games_tcp_ports) / sizeof(uint16_t); i++)
unsigned long size = sizeof(IP_INTERFACE_INFO);
IP_INTERFACE_INFO *infos = (IP_INTERFACE_INFO *)malloc(size);
err = GetInterfaceInfo(infos, &size);
if (err == ERROR_INSUFFICIENT_BUFFER)
{
free(infos);
infos = (IP_INTERFACE_INFO *)malloc(size);
err = GetInterfaceInfo(infos, &size);
if (err != NO_ERROR)
{
printf("GetInterfaceInfo failed error %d\n", err);
infos->NumAdapters = 0;
}
}
const char *tap_guid = pico_tap_get_guid(tap);
wchar_t wtap_guid[40];
MultiByteToWideChar(CP_UTF8, 0, tap_guid, strlen(tap_guid), &wtap_guid[0], 40);
DWORD tap_idx = -1; // 11;
for (int i = 0; i < infos->NumAdapters; i++)
{
printf("Found interface %ls index %d\n", infos->Adapter[i].Name, infos->Adapter[i].Index);
if (wcsstr(infos->Adapter[i].Name, wtap_guid) != NULL)
{
tap_idx = infos->Adapter[i].Index;
break;
}
}
free(infos);
// Set the TAP interface IP address
pico_string_to_ipv4("192.168.166.1", &ipaddr.addr);
pico_string_to_ipv4("255.255.255.0", &netmask.addr);
unsigned long nte_context, nte_instance;
err = AddIPAddress(ipaddr.addr, netmask.addr, tap_idx, &nte_context, &nte_instance);
if (err == ERROR_OBJECT_ALREADY_EXISTS)
printf("TAP IP address already set\n");
else if (err != NO_ERROR)
printf("AddIpAddress failed with error %d\n", err);
else
printf("TAP IP address set\n");
// Create a route to the DC through the TAP interface
if (tap_idx != -1)
{
MIB_IPFORWARDROW fwd;
memset(&fwd, 0, sizeof(fwd));
fwd.dwForwardDest = dcaddr.addr;
fwd.dwForwardMask = 0xffffffff;
fwd.dwForwardIfIndex = tap_idx;
fwd.dwForwardProto = MIB_IPPROTO_NETMGMT;
fwd.dwForwardAge = INFINITE;
fwd.dwForwardMetric1 = 500;
err = CreateIpForwardEntry(&fwd);
if (err == ERROR_OBJECT_ALREADY_EXISTS)
printf("IP forward entry already exists\n");
else if (err != NO_ERROR)
printf("CreateIpForwardEntry failed with error %d\n", err);
else
printf("IP forward entry created\n");
}
#endif
#endif
#ifdef NETWORK_TUN
// TUN
// tun config:
// # ip tuntap add mode tun user joe
// # ip link set tun0 up
// # ip route add <IP>/32 dev tun0 # where <IP> is the value of network:IP in emu.cfg./. This also allows proxy arp
// # echo '1' >/proc/sys/net/ipv4/conf/all/proxy_arp
// (or ...conf/tun0/proxy_arp and ...conf/eth0/proxy_arp only)
tun = pico_tun_create("tun0");
if (!tun)
{ {
stop_pico(); uint16_t port = short_be(games_tcp_ports[i]);
return false; saddr.sin_port = port;
int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (::bind(sockfd, (struct sockaddr *)&saddr, saddr_len) < 0)
{
perror("bind");
close(sockfd);
continue;
}
if (listen(sockfd, 5) < 0)
{
perror("listen");
close(sockfd);
continue;
}
set_non_blocking(sockfd);
tcp_listening_sockets[port] = sockfd;
} }
pico_string_to_ipv4("192.168.166.1", &ipaddr.addr);
pico_string_to_ipv4("255.255.255.255", &netmask.addr);
pico_ipv4_link_add(tun, ipaddr, netmask);
#endif
// Default route
pico_string_to_ipv4("192.168.166.1", &ipaddr.addr);
pico_ipv4_route_add(zero, zero, ipaddr, 1, NULL);
pico_ppp_set_serial_read(ppp, modem_read); pico_ppp_set_serial_read(ppp, modem_read);
pico_ppp_set_serial_write(ppp, modem_write); pico_ppp_set_serial_write(ppp, modem_write);
pico_ppp_set_serial_set_speed(ppp, modem_set_speed); pico_ppp_set_serial_set_speed(ppp, modem_set_speed);
pico_ppp_connect(ppp); pico_ppp_connect(ppp);
#ifdef DHCP while (pico_thread_running)
if (pico_dhcp_initiate_negotiation(tap, &callback_dhcpclient, &dhcpclient_xid) < 0)
{ {
printf("%s: error initiating negotiation: %s\n", __FUNCTION__, strerror(pico_err)); read_native_sockets();
return false; pico_stack_tick();
check_dns_entries();
usleep(1000);
} }
#endif
for (auto it = tcp_listening_sockets.begin(); it != tcp_listening_sockets.end(); it++)
closesocket(it->second);
close_native_sockets();
pico_socket_close(pico_tcp_socket);
pico_socket_close(pico_udp_socket);
if (ppp)
{
pico_ppp_destroy(ppp);
ppp = NULL;
}
return NULL;
}
static cThread pico_thread(pico_thread_func, NULL);
bool start_pico()
{
pico_thread_running = true;
pico_thread.Start();
return true; return true;
} }
void stop_pico() void stop_pico()
{ {
#ifndef _WIN32 pico_thread_running = false;
if (ppp) pico_thread.WaitToEnd();
{
pico_ppp_destroy(ppp);
ppp = NULL;
}
if (tap)
{
pico_device_destroy(tap);
tap = NULL;
}
if (tun)
{
pico_device_destroy(tun);
tun = NULL;
}
#endif
} }
#else #else

View File

@ -1,3 +1,24 @@
/*
Created on: Sep 15, 2018
Copyright 2018 flyinghead
This file is part of reicast.
reicast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
reicast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with reicast. If not, see <https://www.gnu.org/licenses/>.
*/
bool start_pico(); bool start_pico();
void stop_pico(); void stop_pico();
void write_pico(u8 b); void write_pico(u8 b);

View File

@ -1,257 +0,0 @@
/*
pppd.cpp
Created on: Sep 10, 2018
Copyright 2018 flyinghead
This file is part of reicast.
reicast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
reicast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with reicast. If not, see <https://www.gnu.org/licenses/>.
*/
#include "types.h"
#if HOST_OS == OS_LINUX
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include <fcntl.h>
#include <bitset>
#include <string>
#include "oslib/oslib.h"
#include "pppd.h"
static int pppd_pid;
static int inpipe = -1;
static int outpipe = -1;
static u8 in_buffer[128];
static int in_bufsize;
static int in_bufindex;
static bool v42_negotiate = false;
static u32 v42_odp_idx;
static u32 v42_odp_count;
const u8 v42_odp[] { 0x45, 0xfc, 0x17, 0xf9, 0x5f, 0xc4, 0x7f, 0x91, 0xff };
double last_adp_sent;
void start_pppd()
{
int inpipefd[2];
int outpipefd[2];
if (pipe(inpipefd) || pipe(outpipefd))
{
perror("pipe");
return;
}
pppd_pid = fork();
if (pppd_pid < 0)
{
perror("fork");
close(inpipefd[0]);
close(inpipefd[1]);
close(outpipefd[0]);
close(outpipefd[1]);
return;
}
if (pppd_pid == 0)
{
// Child
close(inpipefd[0]);
dup2(inpipefd[1], 1);
close(outpipefd[1]);
dup2(outpipefd[0], 0);
execl("/usr/sbin/pppd", "pppd", "notty", NULL);
perror("execl");
exit(1);
}
// Parent
inpipe = inpipefd[0];
outpipe = outpipefd[1];
fcntl(inpipe, F_SETFL, fcntl(inpipe, F_GETFL) | O_NONBLOCK);
in_bufindex = 0;
in_bufsize = 1;
in_buffer[0] = 0;
}
void stop_pppd()
{
if (pppd_pid > 0)
{
kill(SIGTERM, pppd_pid);
// FIXME wait / waitpid
pppd_pid = 0;
}
if (inpipe >= 0)
{
close(inpipe);
inpipe = -1;
}
if (outpipe >= 0)
{
close(outpipe);
outpipe = -1;
}
}
static u8 reverse_bits(u8 x)
{
x = ((x * 0x0802 & 0x22110) | (x * 0x8020 & 0x88440)) * 0x10101 >> 16;
return x & 0xFF;
}
static void send_v42_adp()
{
in_bufsize = 0;
in_bufindex = 0;
// V.42 disabled E, 8-16 ones, NULL, 8-16 ones // FIXME leading one is a test
const std::string adp_nov42("1" "0101000101" "11111111" "0000000001" "11111111");
const std::string adp_v42("1" "0101000101" "11111111" "0110000101" "11111111");
const std::string& adp = adp_nov42;
const std::string ones("1111111111111111");
int adp_idx = 0;
for (int i = 0; i < 10; )
{
int end = min(adp_idx + 8, (int)adp.length());
std::string binary = adp.substr(adp_idx, end - adp_idx);
adp_idx += 8;
if (adp_idx > end)
{
adp_idx -= adp.length();
if (i == 9)
binary += ones.substr(0, adp_idx);
else
binary += adp.substr(0, adp_idx);
i++;
}
else
{
if (adp_idx >= adp.length())
{
adp_idx = 0;
i++;
}
}
std::bitset<8> bs(binary);
in_buffer[in_bufsize++] = reverse_bits(bs.to_ulong());
if (in_bufsize == sizeof(in_buffer))
{
printf("PPPD in buffer overflow\n");
return;
}
}
v42_negotiate = false;
}
void write_pppd(u8 b)
{
if (v42_negotiate)
{
if (b == v42_odp[v42_odp_idx])
{
v42_odp_idx++;
if (v42_odp_idx >= sizeof(v42_odp))
{
v42_odp_count++;
if (v42_odp_count >= 2)
{
if (last_adp_sent == 0 || os_GetSeconds() - last_adp_sent > 0.75) // T400
{
printf("PPPD V42 2 ODP received. Sending ADP\n");
last_adp_sent = os_GetSeconds();
send_v42_adp();
}
v42_odp_count = 0;
}
v42_odp_idx = 0;
}
}
else
{
v42_odp_idx = 0;
printf("PPPD ignored byte %02x\n", b);
}
return;
}
int rc = write(outpipe, &b, 1);
if (rc < 0)
perror("write outpipe");
else if (rc == 0)
printf("pppd EOF on outpipe\n");
}
int read_pppd()
{
if (in_bufindex == in_bufsize)
{
in_bufindex = 0;
in_bufsize = 0;
if (v42_negotiate)
return -1;
int rc = read(inpipe, in_buffer, sizeof(in_buffer));
if (rc < 0)
{
if (errno != EWOULDBLOCK)
perror("read inpipe");
return rc;
}
else if (rc == 0)
{
printf("pppd EOF on inpipe\n");
return -1;
}
in_bufsize = rc;
in_bufindex = 0;
}
return in_buffer[in_bufindex++];
}
int avail_pppd()
{
return in_bufsize - in_bufindex;
}
#else // not Linux
void start_pppd()
{
}
void stop_pppd()
{
}
void write_pppd(u8 b)
{
}
int read_pppd()
{
return -1;
}
int avail_pppd() {
return 0;
}
#endif

View File

@ -1,34 +0,0 @@
/*
pppd.h
Created on: Sep 10, 2018
Copyright 2018 flyinghead
This file is part of reicast.
reicast is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
reicast is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with reicast. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef CORE_HW_MODEM_PPPD_H_
#define CORE_HW_MODEM_PPPD_H_
#include "types.h"
void start_pppd();
void stop_pppd();
void write_pppd(u8 b);
int read_pppd();
int avail_pppd();
#endif /* CORE_HW_MODEM_PPPD_H_ */