From fbc7459c2c3a86a4d94e19463417b759900ff51c Mon Sep 17 00:00:00 2001 From: Vijay Kumar Banerjee Date: Wed, 3 Feb 2021 19:46:50 -0700 Subject: Initial Commit: Add all files from RTEMS libnetworking directory --- rtems/bootp.h | 44 ++ rtems/bsdnet/_types.h | 41 + rtems/bsdnet/servers.h | 18 + rtems/dhcp.h | 47 ++ rtems/mkrootfs.c | 248 ++++++ rtems/mkrootfs.h | 80 ++ rtems/rtems_bootp.c | 42 ++ rtems/rtems_bsdnet.h | 339 +++++++++ rtems/rtems_bsdnet_internal.h | 285 +++++++ rtems/rtems_bsdnet_malloc_starvation.c | 19 + rtems/rtems_dhcp.c | 1287 ++++++++++++++++++++++++++++++++ rtems/rtems_dhcp_failsafe.c | 380 ++++++++++ rtems/rtems_dhcp_failsafe.h | 64 ++ rtems/rtems_glue.c | 1264 +++++++++++++++++++++++++++++++ rtems/rtems_malloc_mbuf.c | 33 + rtems/rtems_mii_ioctl.c | 172 +++++ rtems/rtems_mii_ioctl.h | 139 ++++ rtems/rtems_mii_ioctl_kern.c | 260 +++++++ rtems/rtems_netdb.h | 52 ++ rtems/rtems_netinet_in.h | 80 ++ rtems/rtems_select.c | 179 +++++ rtems/rtems_showicmpstat.c | 70 ++ rtems/rtems_showifstat.c | 157 ++++ rtems/rtems_showipstat.c | 68 ++ rtems/rtems_showmbuf.c | 69 ++ rtems/rtems_showroute.c | 240 ++++++ rtems/rtems_showtcpstat.c | 106 +++ rtems/rtems_showudpstat.c | 52 ++ rtems/rtems_socketpair.c | 53 ++ rtems/rtems_syscall.c | 835 +++++++++++++++++++++ rtems/rtems_syscall.h | 70 ++ rtems/rtems_syscall_api.c | 22 + rtems/sghostname.c | 54 ++ 33 files changed, 6869 insertions(+) create mode 100644 rtems/bootp.h create mode 100644 rtems/bsdnet/_types.h create mode 100644 rtems/bsdnet/servers.h create mode 100644 rtems/dhcp.h create mode 100644 rtems/mkrootfs.c create mode 100644 rtems/mkrootfs.h create mode 100644 rtems/rtems_bootp.c create mode 100644 rtems/rtems_bsdnet.h create mode 100644 rtems/rtems_bsdnet_internal.h create mode 100644 rtems/rtems_bsdnet_malloc_starvation.c create mode 100644 rtems/rtems_dhcp.c create mode 100644 rtems/rtems_dhcp_failsafe.c create mode 100644 rtems/rtems_dhcp_failsafe.h create mode 100644 rtems/rtems_glue.c create mode 100644 rtems/rtems_malloc_mbuf.c create mode 100644 rtems/rtems_mii_ioctl.c create mode 100644 rtems/rtems_mii_ioctl.h create mode 100644 rtems/rtems_mii_ioctl_kern.c create mode 100644 rtems/rtems_netdb.h create mode 100644 rtems/rtems_netinet_in.h create mode 100644 rtems/rtems_select.c create mode 100644 rtems/rtems_showicmpstat.c create mode 100644 rtems/rtems_showifstat.c create mode 100644 rtems/rtems_showipstat.c create mode 100644 rtems/rtems_showmbuf.c create mode 100644 rtems/rtems_showroute.c create mode 100644 rtems/rtems_showtcpstat.c create mode 100644 rtems/rtems_showudpstat.c create mode 100644 rtems/rtems_socketpair.c create mode 100644 rtems/rtems_syscall.c create mode 100644 rtems/rtems_syscall.h create mode 100644 rtems/rtems_syscall_api.c create mode 100644 rtems/sghostname.c (limited to 'rtems') diff --git a/rtems/bootp.h b/rtems/bootp.h new file mode 100644 index 0000000..4891430 --- /dev/null +++ b/rtems/bootp.h @@ -0,0 +1,44 @@ +/* Subroutines from cpukit/libnetworking/nfs/bootp_subr.c */ + +#if !defined (__RTEMS_BOOTP_H__) +#define __RTEMS_BOOTP_H__ + +#include +#include + +#if __cplusplus +extern "C" +{ +#endif + +struct bootp_packet; +struct proc; +struct ifreq; +struct socket; +struct sockaddr_in; + +bool bootpc_init(bool, bool); + +int bootpc_call( + struct bootp_packet *call, + struct bootp_packet *reply, + struct proc *procp, + const void *exp_vend, + size_t exp_vend_len); +int bootpc_fakeup_interface(struct ifreq *ireq, + struct socket *so, + struct proc *procp); +int bootpc_adjust_interface(struct ifreq *ireq, + struct socket *so, + struct sockaddr_in *myaddr, + struct sockaddr_in *netmask, + struct sockaddr_in *gw, + struct proc *procp); + +void *bootp_strdup_realloc(char *dst, const char *src); + +#if __cplusplus +} +#endif + +#endif diff --git a/rtems/bsdnet/_types.h b/rtems/bsdnet/_types.h new file mode 100644 index 0000000..d557e31 --- /dev/null +++ b/rtems/bsdnet/_types.h @@ -0,0 +1,41 @@ +/*- + * Copyright (c) 2002 Mike Barcroft + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/sys/sys/_types.h,v 1.21 2005/03/22 01:19:17 das Exp $ + */ + + +#ifndef _RTEMS_BSDNET__TYPES_H_ +#define _RTEMS_BSDNET__TYPES_H_ + +#include + +/* + * Standard type definitions. + */ +typedef uint8_t __sa_family_t; +typedef uint32_t __socklen_t; + +#endif /* !_RTEMS_BSDNET_TYPES_H_ */ diff --git a/rtems/bsdnet/servers.h b/rtems/bsdnet/servers.h new file mode 100644 index 0000000..a9db74a --- /dev/null +++ b/rtems/bsdnet/servers.h @@ -0,0 +1,18 @@ +/** + * @file + */ + + +#ifndef _RTEMS_BSDNET_SERVERS_H +#define _RTEMS_BSDNET_SERVERS_H + +extern struct in_addr *rtems_bsdnet_ntpserver; +extern int rtems_bsdnet_ntpserver_count; + +/* + * Network configuration + */ +extern struct in_addr *rtems_bsdnet_nameserver; +extern int rtems_bsdnet_nameserver_count; + +#endif /* _RTEMS_BSDNET_SERVERS_H */ diff --git a/rtems/dhcp.h b/rtems/dhcp.h new file mode 100644 index 0000000..eb7f42d --- /dev/null +++ b/rtems/dhcp.h @@ -0,0 +1,47 @@ +/* + ------------------------------------------------------------------------ + + Copyright Cybertec Pty Ltd, 2005 + All rights reserved Cybertec Pty Ltd, 2005 + + This software with is provided ``as is'' and with NO WARRANTY. + + ------------------------------------------------------------------------ + */ + +/** + @file + + DHCP Server interface. +*/ + +#if !defined (__RTEMS_DHCP_H__) +#define __RTEMS_DHCP_H__ + +#if __cplusplus +extern "C" +{ +#endif + +/* + * Perform DHCP. + */ +void rtems_bsdnet_do_dhcp (void); +int rtems_bsdnet_do_dhcp_timeout (void); +void rtems_bsdnet_dhcp_down (void); + +/* + * Maintain a DHCP offer that has already been accepted. + */ +void rtems_bsdnet_do_dhcp_refresh_only (unsigned long xid, + unsigned long lease_time, + unsigned long elapsed_time, + unsigned long ip_address, + unsigned long srv_address, + const char *hostname); + +#if __cplusplus +} +#endif + +#endif diff --git a/rtems/mkrootfs.c b/rtems/mkrootfs.c new file mode 100644 index 0000000..9414eae --- /dev/null +++ b/rtems/mkrootfs.c @@ -0,0 +1,248 @@ +#include + +/* + ------------------------------------------------------------------------ + + Copyright Cybertec Pty Ltd, 2000 + All rights reserved Cybertec Pty Ltd, 2000 + + COPYRIGHT (c) 1989-1998. + On-Line Applications Research Corporation (OAR). + + The license and distribution terms for this file may be + found in the file LICENSE in this distribution or at + + http://www.rtems.org/license/LICENSE. + + This software with is provided ``as is'' and with NO WARRANTY. + + ------------------------------------------------------------------------ + + Set of helpers when creating a root file system. The root filesystem + in RTEMS is the In Memory Filesystem (IMFS). We could copy an exiting + filesystem to here, how-ever a number of files can have target + specific initialisation info which we need to write. + + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * A table a list of names and their modes. + */ + +typedef struct rtems_rootfs_dir_table +{ + const char *name; + mode_t mode; +} rtems_rootfs_dir_table; + +/* + * Table of directorys to make. + */ + +static const rtems_rootfs_dir_table default_directories[] = +{ + { "/bin", S_IFDIR | S_IRWXU | S_IXGRP | S_IRGRP | S_IROTH | S_IXOTH }, + { "/etc", S_IFDIR | S_IRWXU | S_IXGRP | S_IRGRP | S_IROTH | S_IXOTH }, + { "/dev", S_IFDIR | S_IRWXU | S_IXGRP | S_IRGRP | S_IROTH | S_IXOTH }, + { "/usr/bin", S_IFDIR | S_IRWXU | S_IXGRP | S_IRGRP | S_IROTH | S_IXOTH } +}; + +#define MKFILE_MODE (S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH) +#define MKDIR_MODE (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) + +/* + * Create enough files to support the networking stack. + * Points to a table of strings. + */ + +int +rtems_rootfs_file_append (const char *file, + mode_t omode, + const int line_cnt, + const char **lines) +{ + struct stat sb; + int fd; + int i; + + /* + * See is a file exists. If it does not, create the + * file and the path to the file. + */ + + fd = -1; + + if (stat(file, &sb)) + { + if (errno == ENOENT) + { + /* + * Get the path to the file if one exists and create the + * path. If it exists nothing happens. + */ + + size_t i = strlen (file); + + while (i) + { + if (file[i] == '/') + { + char path[128]; + + if (i >= sizeof path) + { + printf ("root fs, path too long `%s'\n", file); + return -1; + } + + strncpy (path, file, i); + path[i] = '\0'; + + if (rtems_mkdir (path, MKDIR_MODE)) + return -1; + break; + } + i--; + } + + if ((fd = open (file, O_CREAT | O_APPEND | O_WRONLY, omode)) < 0) + { + printf ("root fs, cannot create file `%s' : %s\n", + file, strerror (errno)); + return -1; + } + } + } + + if (fd < 0) + { + if ((fd = open (file, O_APPEND | O_WRONLY)) < 0) + { + printf ("root fs, cannot open file `%s' : %s\n", + file, strerror (errno)); + return -1; + } + } + + for (i = 0; i < line_cnt; i++) + { + size_t len = strlen (lines[i]); + + if (len) + { + if (write (fd, lines[i], strlen (lines[i])) < 0) + { + close (fd); + printf ("root fs, cannot write to `%s' : %s\n", + file, strerror (errno)); + return -1; + } + } + } + + return close (fd); +} + +/* + * Write hosts record. + */ + +int +rtems_rootfs_append_host_rec (in_addr_t cip, + const char *cname, + const char *dname) +{ + char buf[128]; + char *bufp = buf; + const char *bufl[1]; + struct in_addr ip; + + ip.s_addr = cip; + + if (cname && strlen (cname)) + { + char addrbuf[INET_ADDRSTRLEN]; + + snprintf (bufp, sizeof (buf), "%s\t\t%s", inet_ntoa_r (ip, addrbuf), cname); + bufp += strlen (buf); + + if (dname && strlen (dname)) + { + snprintf (bufp, sizeof (buf), "\t\t%s.%s", cname, dname); + bufp += strlen (buf); + } + + strcat (buf, "\n"); + + bufl[0] = buf; + + if (rtems_rootfs_file_append ("/etc/hosts", MKFILE_MODE, 1, bufl) < 0) + return -1; + } + else + { + printf ("rootfs hosts rec append, no cname supplied\n"); + return -1; + } + + return 0; +} + +/* + * Create a root file system. + */ + +int +rtems_create_root_fs (void) +{ + const char *lines[1]; + size_t i; + + /* + * Create the directories. + */ + + for (i = 0; + i < (sizeof (default_directories) / sizeof (rtems_rootfs_dir_table)); + i++) + if (rtems_mkdir (default_directories[i].name, + default_directories[i].mode)) + return -1; + + /* + * The TCP/IP stack likes this one. If DNS does not work + * use the host file. + */ + + lines[0] = "hosts,bind\n"; + + if (rtems_rootfs_file_append ("/etc/host.conf", MKFILE_MODE, 1, lines)) + return -1; + + /* + * Create a `/etc/hosts' file. + */ + + if (rtems_rootfs_append_host_rec (htonl (0x7f000001), "localhost", "localdomain")) + return -1; + + return 0; +} diff --git a/rtems/mkrootfs.h b/rtems/mkrootfs.h new file mode 100644 index 0000000..1b1cb29 --- /dev/null +++ b/rtems/mkrootfs.h @@ -0,0 +1,80 @@ +/** + * @file + * + * RTEMS Root FS creation support. + */ + +/* + Copyright Cybertec Pty Ltd, 2000 + All rights reserved Cybertec Pty Ltd, 2000 + + COPYRIGHT (c) 1989-1998. + On-Line Applications Research Corporation (OAR). + + The license and distribution terms for this file may be + found in the file LICENSE in this distribution or at + http://www.rtems.org/license/LICENSE. + + This software with is provided ``as is'' and with NO WARRANTY. +*/ + +#ifndef _RTEMS_MKROOTFS_H +#define _RTEMS_MKROOTFS_H + +#include /* in_addr_t */ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Appends the lines to the a file. Create the file + * and builds the path if it does not exist. + * + * @param file + * @param omode + * @param line_cnt + * @param lines + * + * @return 0 on success, -1 on error + */ + +int +rtems_rootfs_file_append (const char *file, + mode_t omode, + const int line_cnt, + const char **lines); + +/** + * @brief Helper for bulding an /etc/hosts file. + * + * @param cip + * @param cname + * @param dname + * + * @return 0 on success, -1 on error + */ + +int +rtems_rootfs_append_host_rec (in_addr_t cip, + const char *cname, + const char *dname); + +/** + * Create a few common directories, plus a: + * /etc/passwd, /etc/group, /etc/host.conf, and + * /etc/hosts file. + * + * @return 0 on success, -1 on error + */ + +int +rtems_create_root_fs ( void ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/rtems/rtems_bootp.c b/rtems/rtems_bootp.c new file mode 100644 index 0000000..134ae90 --- /dev/null +++ b/rtems/rtems_bootp.c @@ -0,0 +1,42 @@ +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include "rtems/bootp.h" + +/* + * Perform a BOOTP request + */ +void +rtems_bsdnet_do_bootp (void) +{ + bool ok; + rtems_bsdnet_semaphore_obtain (); + ok = bootpc_init (false, true); + rtems_bsdnet_semaphore_release (); + if (!ok) + panic ("rtems_bsdnet_do_bootp: bootp failed"); +} + +/* + * Perform a BOOTP request and update "standard" files in /etc + * with the results. + */ +void +rtems_bsdnet_do_bootp_and_rootfs (void) +{ + bool ok; + rtems_bsdnet_semaphore_obtain (); + ok = bootpc_init (true, true); + rtems_bsdnet_semaphore_release (); + if (!ok) + panic ("rtems_bsdnet_do_bootp_and_rootfs: bootp failed"); +} diff --git a/rtems/rtems_bsdnet.h b/rtems/rtems_bsdnet.h new file mode 100644 index 0000000..4607f42 --- /dev/null +++ b/rtems/rtems_bsdnet.h @@ -0,0 +1,339 @@ +/** + * @file + */ + + +#ifndef _RTEMS_BSDNET_H +#define _RTEMS_BSDNET_H + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Values that may be obtained by BOOTP + */ +extern struct in_addr rtems_bsdnet_bootp_server_address; +extern char *rtems_bsdnet_bootp_server_name; +extern char *rtems_bsdnet_bootp_boot_file_name; +extern char *rtems_bsdnet_bootp_cmdline; +extern int32_t rtems_bsdnet_timeoffset; + +/* + * Manipulate routing tables + */ +struct sockaddr; +struct rtentry; +int rtems_bsdnet_rtrequest ( + int req, + struct sockaddr *dst, + struct sockaddr *gateway, + struct sockaddr *netmask, + int flags, + struct rtentry **net_nrt); + +/* + * Diagnostics + */ +void rtems_bsdnet_show_inet_routes (void); +void rtems_bsdnet_show_mbuf_stats (void); +void rtems_bsdnet_show_if_stats (void); +void rtems_bsdnet_show_ip_stats (void); +void rtems_bsdnet_show_icmp_stats (void); +void rtems_bsdnet_show_udp_stats (void); +void rtems_bsdnet_show_tcp_stats (void); + +/* + * Network configuration + */ +struct rtems_bsdnet_ifconfig { + /* + * These three entries must be supplied for each interface. + */ + char *name; + + /* + * This function now handles attaching and detaching an interface. + * The parameter attaching indicates the operation being invoked. + * For older attach functions which do not have the extra parameter + * it will be ignored. + */ + int (*attach)(struct rtems_bsdnet_ifconfig *conf, int attaching); + + /* + * Link to next interface + */ + struct rtems_bsdnet_ifconfig *next; + + /* + * The following entries may be obtained + * from BOOTP or explicitily supplied. + */ + char *ip_address; + char *ip_netmask; + void *hardware_address; + + /* + * The driver assigns defaults values to the following + * entries if they are not explicitly supplied. + */ + int ignore_broadcast; + int mtu; + int rbuf_count; + int xbuf_count; + + /* + * For external ethernet controller board the following + * parameters are needed + */ + unsigned int port; /* port of the board */ + unsigned int irno; /* irq of the board */ + unsigned int bpar; /* memory of the board */ + + /* + * Driver control block pointer. Typcially this points to the driver's + * controlling structure. You set this when you have the structure allocated + * externally to the driver. + */ + void *drv_ctrl; + +}; + +struct rtems_bsdnet_config { + /* + * This entry points to the head of the ifconfig chain. + */ + struct rtems_bsdnet_ifconfig *ifconfig; + + /* + * This entry should be rtems_bsdnet_do_bootp if BOOTP + * is being used to configure the network, and NULL + * if BOOTP is not being used. + */ + void (*bootp)(void); + + /* + * The remaining items can be initialized to 0, in + * which case the default value will be used. + */ + rtems_task_priority network_task_priority; /* 100 */ + unsigned long mbuf_bytecount; /* 64 kbytes */ + unsigned long mbuf_cluster_bytecount; /* 128 kbytes */ + char *hostname; /* BOOTP */ + char *domainname; /* BOOTP */ + char *gateway; /* BOOTP */ + char *log_host; /* BOOTP */ + char *name_server[3]; /* BOOTP */ + char *ntp_server[3]; /* BOOTP */ + /* + * Default "multiplier" on buffer size. This is + * claimed by the TCP/IP implementation to be for + * efficiency but you will have to measure the + * benefit for buffering beyond double buffering + * in your own application. + * + * The default value is 2. + * + * See kern/uipc_socket2.c for details. + */ + unsigned long sb_efficiency; + /* + * Default UDP buffer sizes PER SOCKET!! + * + * TX = 9216 -- max datagram size + * RX = 40 * (1024 + sizeof(struct sockaddr_in)) + * + * See netinet/udp_usrreq.c for details + */ + unsigned long udp_tx_buf_size; + unsigned long udp_rx_buf_size; + /* + * Default UDP buffer sizes PER SOCKET!! + * + * TX = 16 * 1024 + * RX = 16 * 1024 + * + * See netinet/tcp_usrreq.c for details + */ + unsigned long tcp_tx_buf_size; + unsigned long tcp_rx_buf_size; + + /* + * Default Network Tasks CPU Affinity + */ +#ifdef RTEMS_SMP + const cpu_set_t *network_task_cpuset; + size_t network_task_cpuset_size; +#endif +}; + +/* + * Default global device configuration structure. This is scanned + * by the initialize network function. Check the network demo's for + * an example of the structure. Like the RTEMS configuration tables, + * they are not part of RTEMS but part of your application or bsp + * code. + */ +extern struct rtems_bsdnet_config rtems_bsdnet_config; + +/* + * Initialise the BSD stack, attach and `up' interfaces + * in the `rtems_bsdnet_config'. RTEMS must already be initialised. + */ +int rtems_bsdnet_initialize_network (void); + +/* + * Dynamic interface control. Drivers must free any resources such as + * memory, interrupts, io regions claimed during the `attach' and/or + * `up' operations when asked to `detach'. + * You must configure the interface after attaching it. + */ +void rtems_bsdnet_attach (struct rtems_bsdnet_ifconfig *ifconfig); +void rtems_bsdnet_detach (struct rtems_bsdnet_ifconfig *ifconfig); + +/* + * Interface configuration. The commands are listed in `sys/sockio.h'. + */ +int rtems_bsdnet_ifconfig (const char *ifname, uint32_t cmd, void *param); + +void rtems_bsdnet_do_bootp (void); +void rtems_bsdnet_do_bootp_and_rootfs (void); + +/* NTP tuning parameters */ +extern int rtems_bsdnet_ntp_retry_count; +extern int rtems_bsdnet_ntp_timeout_secs; +extern int rtems_bsdnet_ntp_bcast_timeout_secs; + + +struct timestamp { + uint32_t integer; + uint32_t fraction; +}; + +/* Data is passed in network byte order */ +struct ntpPacketSmall { + uint8_t li_vn_mode; + uint8_t stratum; + int8_t poll_interval; + int8_t precision; + int32_t root_delay; + int32_t root_dispersion; + char reference_identifier[4]; + struct timestamp reference_timestamp; + struct timestamp originate_timestamp; + struct timestamp receive_timestamp; + struct timestamp transmit_timestamp; +}; + +/* NOTE: packet data is *only* accessible from the callback + * + * 'callback' is invoked twice, once prior to sending a request + * to the server and one more time after receiving a valid reply. + * This allows for the user to measure round-trip times. + * + * Semantics of the 'state' parameter: + * + * state == 1: 1st call, just prior to sending request. The + * packet has been set up already but may be + * modified by the callback (e.g. to set the originate + * timestamp). + * state == -1: 1st call - no request will be sent but we'll + * wait for a reply from a broadcast server. The + * packet has not been set up. + * state == 0: 2nd call. The user is responsible for keeping track + * of the 'state' during the first call in order to + * know if it makes sense to calculate 'round-trip' times. + * + * RETURN VALUE: the callback should return 0 if processing the packet was + * successful and -1 on error in which case rtems_bsdnet_get_ntp() + * may try another server. + */ +typedef int (*rtems_bsdnet_ntp_callback_t)( + struct ntpPacketSmall *packet, + int state, + void *usr_data); + +/* Obtain time from a NTP server and call user callback to process data; + * socket parameter may be -1 to request the routine to open and close its own socket. + * Networking parameters as configured are used... + * + * It is legal to pass a NULL callback pointer. In this case, a default callback + * is used which determines the current time by contacting an NTP server. The current + * time is converted to a 'struct timespec' (seconds/nanoseconds) and passed into *usr_data. + * The caller is responsible for providing a memory area >= sizeof(struct timespec). + * + * RETURNS: 0 on success, -1 on failure. + */ +int rtems_bsdnet_get_ntp(int socket, rtems_bsdnet_ntp_callback_t callback, void *usr_data); + +int rtems_bsdnet_synchronize_ntp (int interval, rtems_task_priority priority); + +/* + * Callback to report BSD malloc starvation. + * The default implementation just prints a message but an application + * can provide its own version. + */ +void rtems_bsdnet_malloc_starvation(void); + +/* + * mbuf malloc interface to enable custom allocation of mbuf's + * + * May be declared in user code. If not, then the default is to + * malloc. + */ +void* rtems_bsdnet_malloc_mbuf(size_t size, int type); + +/* + * Possible values of the type parameter to rtems_bsdnet_malloc_mbuf to assist + * in allocation of the structure. + */ +#define MBUF_MALLOC_NMBCLUSTERS (0) +#define MBUF_MALLOC_MCLREFCNT (1) +#define MBUF_MALLOC_MBUF (2) + +/* + * RTEMS-specific socket wake-up additions previously part of . + * + * An alternative in the LibBSD network stack for this is KQUEUE(2). + */ + +#define SO_SNDWAKEUP 0x1020 /* wakeup when ready to send */ +#define SO_RCVWAKEUP 0x1021 /* wakeup when ready to receive */ + +/* + * RTEMS additions for setting/getting `tap' function on incoming packets. + */ +struct ifnet; +struct ether_header; +struct mbuf; +struct rtems_tap_ifreq { + char ifr_name[IFNAMSIZ]; /* if name, e.g. "en0" */ + int (*ifr_tap)(struct ifnet *, struct ether_header *, struct mbuf *); +}; +#define SIOCSIFTAP _IOW('i', 88, struct rtems_tap_ifreq) /* set tap function */ +#define SIOCGIFTAP _IOW('i', 89, struct rtems_tap_ifreq) /* get tap function */ + +#define OSIOCGIFADDR _IOWR('i', 13, struct ifreq) /* get ifnet address */ +#define OSIOCGIFDSTADDR _IOWR('i', 15, struct ifreq) /* get p-p address */ +#define OSIOCGIFBRDADDR _IOWR('i', 18, struct ifreq) /* get broadcast addr */ +#define OSIOCGIFCONF _IOWR('i', 20, struct ifconf) /* get ifnet list */ +#define OSIOCGIFNETMASK _IOWR('i', 21, struct ifreq) /* get net addr mask */ + +struct socket; + +struct sockwakeup { + void (*sw_pfn)(struct socket *, void *); + void *sw_arg; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _RTEMS_BSDNET_H */ diff --git a/rtems/rtems_bsdnet_internal.h b/rtems/rtems_bsdnet_internal.h new file mode 100644 index 0000000..d103c56 --- /dev/null +++ b/rtems/rtems_bsdnet_internal.h @@ -0,0 +1,285 @@ +/* + * Declarations to fit FreeBSD to RTEMS. + * + ******************************************************************* + * WARNING * + * This file should *never* be included by any application program * + ******************************************************************* + */ + +#ifndef _RTEMS_RTEMS_BSDNET_INTERNAL_H +#define _RTEMS_RTEMS_BSDNET_INTERNAL_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef __uintptr_t vm_offset_t; +typedef __intptr_t vm_ooffset_t; +typedef __uintptr_t vm_pindex_t; +typedef __uintptr_t vm_size_t; + +#define _BSD_OFF_T_ int32_t +#define _BSD_PID_T_ rtems_id +#define _BSD_VA_LIST_ char * + +/* make sure we get the network versions of these */ +#include +#include +#include +#include + +#include +#include + +struct mdproc { + int md_flags; + int *md_regs; +}; + +/* + * Other RTEMS/BSD glue + */ +struct socket; +extern int soconnsleep (struct socket *so); +extern void soconnwakeup (struct socket *so); +#define splnet() 0 +#define splimp() 0 +#define splx(_s) do { (_s) = 0; (void) (_s); } while(0) + +/* to avoid warnings */ +void *memcpy(void *dest, const void *src, size_t n); +void *memset(void *s, int c, size_t n); + +#define ovbcopy(f,t,n) bcopy(f,t,n) +#define copyout(f,t,n) (memcpy(t,f,n),0) +#define copyin(f,t,n) (memcpy(t,f,n),0) + +#define random() rtems_bsdnet_random() +#define panic rtems_panic +#define suser(a,b) 0 + +#define microtime(_tvp) _Timecounter_Microtime(_tvp) + +#define hz rtems_bsdnet_ticks_per_second +#define tick rtems_bsdnet_microseconds_per_tick + +#define log rtems_bsdnet_log + +/* + * Since we can't have two sys/types.h files, we'll hack around + * and copy the contents of the BSD sys/types.h to here.... + */ + +typedef u_int64_t u_quad_t; /* quads */ +typedef int64_t quad_t; +typedef quad_t * qaddr_t; + +typedef void __sighandler_t(int); +typedef __sighandler_t *sig_t; /* type of pointer to a signal function */ +#define NSIG 32 + +#ifdef _KERNEL +typedef int boolean_t; +#endif + +#ifndef _POSIX_SOURCE +/* + * minor() gives a cookie instead of an index since we don't want to + * change the meanings of bits 0-15 or waste time and space shifting + * bits 16-31 for devices that don't use them. + */ +#define major(x) ((int)(((u_int)(x) >> 8)&0xff)) /* major number */ +#define minor(x) ((int)((x)&0xffff00ff)) /* minor number */ +#define makedev(x,y) ((dev_t)(((x) << 8) | (y))) /* create dev_t */ +#endif + +#ifndef _BYTEORDER_PROTOTYPED +#define _BYTEORDER_PROTOTYPED +__BEGIN_DECLS +__uint32_t htonl(__uint32_t); +__uint16_t htons(__uint16_t); +__uint32_t ntohl(__uint32_t); +__uint16_t ntohs(__uint16_t); +__END_DECLS +#endif + +#ifndef _BYTEORDER_FUNC_DEFINED +#define _BYTEORDER_FUNC_DEFINED +#define htonl(x) __htonl(x) +#define htons(x) __htons(x) +#define ntohl(x) __ntohl(x) +#define ntohs(x) __ntohs(x) +#endif /* !_BYTEORDER_FUNC_DEFINED */ + +#define NTOHS(x) (x) = ntohs(x) +#define HTONS(x) (x) = htons(x) +#define NTOHL(x) (x) = ntohl(x) +#define HTONL(x) (x) = htonl(x) + +in_addr_t inet_addr(const char *); + +typedef quad_t rlim_t; /* resource limit */ +typedef u_int32_t fixpt_t; /* fixed point number */ + +/* + * Forward structure declarations for function prototypes. We include the + * common structures that cross subsystem boundaries here; others are mostly + * used in the same place that the structure is defined. + */ +struct proc; +struct pgrp; +struct ucred; +struct rusage; +struct buf; +struct tty; +struct uio; +struct rtems_bsdnet_ifconfig; + +/* + * Redo kernel memory allocation + */ +#define malloc(size,type,flags) rtems_bsdnet_malloc(size,type,flags) +#define free(ptr,type) rtems_bsdnet_free(ptr,type) +#define timeout(ftn,arg,ticks) rtems_bsdnet_timeout(ftn,arg,ticks) + +#define M_NOWAIT 0x0001 +void *rtems_bsdnet_malloc (size_t size, int type, int flags); +void rtems_bsdnet_free (void *addr, int type); + +void rtems_bsdnet_semaphore_obtain (void); +void rtems_bsdnet_semaphore_release (void); +void rtems_bsdnet_semaphore_obtain_recursive (uint32_t nest_count); +uint32_t rtems_bsdnet_semaphore_release_recursive (void); +void rtems_bsdnet_schednetisr (int n); +int rtems_bsdnet_parse_driver_name (const struct rtems_bsdnet_ifconfig *config, char **namep); + +static inline unsigned long rtems_bsdnet_seconds_since_boot(void) +{ + return (unsigned long) rtems_clock_get_uptime_seconds() + 1UL; +} + +unsigned long rtems_bsdnet_random (void); + +rtems_id rtems_bsdnet_newproc ( + char *name, + int stacksize, + void (*entry)(void *), + void *arg +); + +#ifdef RTEMS_SMP +/* As rtems_bsdnet_newproc() but with ability to set CPU affinity too */ +rtems_id rtems_bsdnet_newproc_affinity ( + char *name, + int stacksize, + void (*entry)(void *), + void *arg, + const cpu_set_t *set, + const size_t setsize +); +#endif + +rtems_status_code rtems_bsdnet_event_receive ( + rtems_event_set event_in, + rtems_option option_set, + rtems_interval ticks, + rtems_event_set *event_out +); + +static inline rtems_status_code rtems_bsdnet_event_send ( + rtems_id task_id, + rtems_event_set event_in +) +{ + return rtems_event_system_send (task_id, event_in); +} + +/* + * Network configuration + */ +extern int rtems_bsdnet_ticks_per_second; +extern int rtems_bsdnet_microseconds_per_tick; +extern struct in_addr rtems_bsdnet_log_host_address; +extern char *rtems_bsdnet_domain_name; + +/* + * Internal IOCTL command + */ +#define SIO_RTEMS_SHOW_STATS _IO('i', 250) + +/* + * Some extra prototypes + */ +int sethostname (const char *name, size_t namelen); +void domaininit (void *); +void ifinit (void *); +void ipintr (void); +void arpintr (void); +int socket (int, int, int); +int ioctl (int, ioctl_command_t, ...); + +/* + * Events used by networking routines. + * Everything will break if the application + * tries to use these events or if the `sleep' + * events are equal to any of the NETISR * events. + */ +#define SBWAIT_EVENT RTEMS_EVENT_SYSTEM_NETWORK_SBWAIT +#define SOSLEEP_EVENT RTEMS_EVENT_SYSTEM_NETWORK_SOSLEEP +#define NETISR_IP_EVENT (1L << NETISR_IP) +#define NETISR_ARP_EVENT (1L << NETISR_ARP) +#define NETISR_EVENTS (NETISR_IP_EVENT|NETISR_ARP_EVENT) +#if (SBWAIT_EVENT & SOSLEEP_EVENT & NETISR_EVENTS & RTEMS_EVENT_SYSTEM_NETWORK_CLOSE) +# error "Network event conflict" +#endif + +struct socket *rtems_bsdnet_fdToSocket(int fd); + +void sysctl_register_all(void *); + +void rtems_set_udp_buffer_sizes(u_long, u_long); + +void rtems_set_tcp_buffer_sizes(u_long, u_long); + +void rtems_set_sb_efficiency(u_long); + +#define IFF_OACTIVE IFF_DRV_OACTIVE +#define IFF_RUNNING IFF_DRV_RUNNING + +struct ifaddr; +void ifafree(struct ifaddr *); + +struct ifnet; +struct mbuf; +struct sockaddr; +struct rtentry; +int looutput(struct ifnet *, + struct mbuf *, struct sockaddr *, struct rtentry *); + +typedef u_long tcp_cc; /* connection count per rfc1644 */ + +#define TCPOPT_TSTAMP_HDR \ + (uint32_t)(((uint32_t)TCPOPT_NOP<<24)| \ + ((uint32_t)TCPOPT_NOP<<16)| \ + ((uint32_t)TCPOPT_TIMESTAMP<<8)| \ + ((uint32_t)TCPOLEN_TIMESTAMP)) + +#define TCPOPT_CC 11 /* CC options: RFC-1644 */ +#define TCPOPT_CCNEW 12 +#define TCPOPT_CCECHO 13 +#define TCPOLEN_CC 6 +#define TCPOLEN_CC_APPA (TCPOLEN_CC+2) +#define TCPOPT_CC_HDR(ccopt) \ + (TCPOPT_NOP<<24|TCPOPT_NOP<<16|(ccopt)<<8|TCPOLEN_CC) + +#ifdef __cplusplus +} +#endif + +#endif /* _RTEMS_RTEMS_BSDNET_INTERNAL_H */ diff --git a/rtems/rtems_bsdnet_malloc_starvation.c b/rtems/rtems_bsdnet_malloc_starvation.c new file mode 100644 index 0000000..c5ccd8c --- /dev/null +++ b/rtems/rtems_bsdnet_malloc_starvation.c @@ -0,0 +1,19 @@ +#include + +/* + * Routine called when malloc() is not succeeding. This can be overridden + * by a BSP. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +void +rtems_bsdnet_malloc_starvation(void) +{ + printf ("rtems_bsdnet_malloc still waiting.\n"); +} diff --git a/rtems/rtems_dhcp.c b/rtems/rtems_dhcp.c new file mode 100644 index 0000000..6a153a4 --- /dev/null +++ b/rtems/rtems_dhcp.c @@ -0,0 +1,1287 @@ +#include + +/* + * DCHP client for RTEMS + * Andrew Bythell, + * based on and uses subroutines from c/src/libnetworking/nfs/bootp_subr.c + */ + +/* + * DHCP task added. + * Brendan Gannon, + */ + +/* + * Added interface to terminate DHCP task, and removed panics. + * Arnout Vandecappelle , Essensium/Mind + * Maarten Van Es , Essensium/Mind + */ + +/* + * Copyright (c) 1995 Gordon Ross, Adam Glass + * Copyright (c) 1992 Regents of the University of California. + * All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratory and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include /* for MAXHOSTNAMELEN */ +#include +#include /* for socreat() soclose() */ +#include + +#include +#include +#include /* for NBO-HBO conversions */ +#include /* for IFT_ETHER */ +#include /* for LLADDR */ + +#include +#include +#include +#include + +#include "rtems/dhcp.h" +#include "rtems/bootp.h" + +#ifndef EALEN +#define EALEN 6 +#endif + +/* + *DHCP flags + */ +#define DHCP_BROADCAST 0x8000 +#define DHCP_UNICAST 0x0000 + +/* + * DHCP Op Codes + */ +#define DHCP_BOOTREQUEST 1 +#define DHCP_BOOTREPLY 2 + +/* + * DHCP Messages + */ +#define DHCP_DISCOVER 1 +#define DHCP_OFFER 2 +#define DHCP_REQUEST 3 +#define DHCP_DECLINE 4 +#define DHCP_ACK 5 +#define DHCP_NACK 6 +#define DHCP_RELEASE 7 + +/* + * DHCP Options + */ +#define DHCP_OPTION_PAD 0 +#define DHCP_SUBNET 1 +#define DHCP_GATEWAY 3 +#define DHCP_DNS 6 +#define DHCP_HOST 12 +#define DHCP_DOMAIN_NAME 15 +#define DHCP_NETMASK 28 +#define DHCP_REQUESTED_IP 50 +#define DHCP_LEASE 51 +#define DHCP_MESSAGE 53 +#define DHCP_SERVER 54 +#define DHCP_PARAMETERS 55 +#define DHCP_OPTION_END 255 + +/* + * Definitions from RFC + */ +struct dhcp_packet +{ + u_int8_t op; + u_int8_t htype; + u_int8_t hlen; + u_int8_t hops; + u_int32_t xid; + u_int16_t secs; + u_int16_t flags; + struct in_addr ciaddr; + struct in_addr yiaddr; + struct in_addr siaddr; + struct in_addr giaddr; + unsigned char chaddr[16]; + char sname[64]; + char file[128]; + unsigned char vend[312]; +}; + +/* + * Variables + */ +static int dhcp_option_overload = 0; +static char dhcp_gotgw = 0; +static char dhcp_gotnetmask = 0; +static char dhcp_gotserver = 0; +static char dhcp_gotlogserver = 0; +static struct sockaddr_in dhcp_netmask; +static struct sockaddr_in dhcp_gw; +static char *dhcp_hostname; +static int dhcp_message_type = 0; +static unsigned long dhcp_lease_time; +static unsigned long dhcp_elapsed_time = 0; +static const char dhcp_magic_cookie[4] = { 99, 130, 83, 99 }; +static const char dhcp_request_parameters[5] = { DHCP_SUBNET, + DHCP_GATEWAY, + DHCP_DNS, + DHCP_HOST, + DHCP_DOMAIN_NAME }; +#define NUM_NAMESERVERS \ + (sizeof rtems_bsdnet_config.name_server / sizeof rtems_bsdnet_config.name_server[0]) +static struct in_addr rtems_dhcpd_nameserver[NUM_NAMESERVERS]; +static int rtems_dhcpd_nameserver_count = 0; + +/* + * Clean any DNS entries add by a DHCP request. + */ +static void +clean_dns_entries (void) +{ + int e; + for (e = 0; e < rtems_dhcpd_nameserver_count; ++e) + { + int n; + for (n = 0; n < rtems_bsdnet_nameserver_count; ++ n) + { + if (memcmp (&rtems_dhcpd_nameserver[e], &rtems_bsdnet_nameserver[n], 4) == 0) + { + if (n < (NUM_NAMESERVERS - 1)) + memmove (&rtems_bsdnet_nameserver[n], + &rtems_bsdnet_nameserver[n + 1], + (NUM_NAMESERVERS - n - 1) * 4); + --rtems_bsdnet_nameserver_count; + } + } + } + rtems_dhcpd_nameserver_count = 0; +} + +/* + * Format an IP address in dotted decimal. + */ +static void +format_ip (unsigned long ip, char* buffer) +{ + sprintf (buffer, + "%lu.%lu.%lu.%lu", + (ip >> 24), + (ip >> 16) & 0xff, + (ip >> 8) & 0xff, + (ip & 0xff)); + + return; +} + +/* + * Print the IP setup + */ +static void +printsetup (const char *iface, + struct in_addr ip_addr, + struct in_addr mask_addr, + struct in_addr srv_addr, + struct in_addr gw_addr) +{ + unsigned long ip; + char ip_str[15]; + + printf ("dhcpc: %s: ", iface); + + ip = ntohl (ip_addr.s_addr); + format_ip (ip, ip_str); + printf ("inet: %-15s ", ip_str); + + ip = ntohl (mask_addr.s_addr); + format_ip (ip, ip_str); + printf ("mask: %-15s\n", ip_str); + + ip = ntohl (srv_addr.s_addr); + format_ip (ip, ip_str); + printf (" srv: %-15s ", ip_str); + + ip = ntohl (gw_addr.s_addr); + format_ip (ip, ip_str); + printf (" gw: %-15s\n", ip_str); + + return; +} + +/* + * Process options from the DHCP packet. + * Based on BOOTP routine. + */ +static void +process_options (unsigned char *optbuf, int optbufSize) +{ + int j = 0; + int len; + int code, ncode; + char *p; + + dhcp_message_type = 0; + + ncode = optbuf[0]; + while (j < optbufSize) + { + code = optbuf[j] = ncode; + if (code == 255) + return; + if (code == 0) + { + j++; + continue; + } + len = optbuf[j + 1]; + j += 2; + + if ((len + j) >= optbufSize) + { + printf ("Truncated field for code %d", code); + return; + } + + ncode = optbuf[j + len]; + optbuf[j + len] = '\0'; + p = (char*) &optbuf[j]; + j += len; + + /* + * Process the option + */ + switch (code) + { + case 1: + /* Subnet mask */ + if (len != 4) { + printf ("dhcpc: subnet mask len is %d\n", len); + continue; + } + memcpy (&dhcp_netmask.sin_addr, p, 4); + dhcp_gotnetmask = 1; + break; + + case 2: + /* Time offset */ + if (len != 4) { + printf ("dhcpc: time offset len is %d\n", len); + continue; + } + memcpy (&rtems_bsdnet_timeoffset, p, 4); + rtems_bsdnet_timeoffset = ntohl (rtems_bsdnet_timeoffset); + break; + + case 3: + /* Routers */ + if (len % 4) { + printf ("dhcpc: Router Len is %d\n", len); + continue; + } + if (len > 0) + { + memcpy (&dhcp_gw.sin_addr, p, 4); + dhcp_gotgw = 1; + } + break; + + case 42: + /* NTP servers */ + if (len % 4) { + printf ("dhcpc: time server Len is %d\n", len); + continue; + } + { + int tlen = 0; + while ((tlen < len) && + (rtems_bsdnet_ntpserver_count < + sizeof rtems_bsdnet_config.ntp_server / + sizeof rtems_bsdnet_config.ntp_server[0])) + { + memcpy (&rtems_bsdnet_ntpserver[rtems_bsdnet_ntpserver_count], + p + tlen, 4); + rtems_bsdnet_ntpserver_count++; + tlen += 4; + } + } + break; + + case 6: + /* Domain Name servers */ + if (len % 4) { + printf ("dhcpc: DNS Len is %d\n", len); + continue; + } + { + int dlen = 0; + while ((dlen < len) && + (rtems_dhcpd_nameserver_count < NUM_NAMESERVERS) && + (rtems_bsdnet_nameserver_count < NUM_NAMESERVERS)) + { + memcpy (&rtems_dhcpd_nameserver + [rtems_dhcpd_nameserver_count], p + dlen, 4); + rtems_dhcpd_nameserver_count++; + memcpy (&rtems_bsdnet_nameserver + [rtems_bsdnet_nameserver_count], p + dlen, 4); + rtems_bsdnet_nameserver_count++; + dlen += 4; + } + } + break; + + case 12: + /* Host name */ + if (len >= MAXHOSTNAMELEN) { + printf ("dhcpc: hostname >= %d bytes\n", MAXHOSTNAMELEN); + len = MAXHOSTNAMELEN-1; + } + if (sethostname (p, len) < 0) { + printf ("dhcpc: can't set host name"); + } + if (dhcp_hostname != NULL) + { + char *tmp = realloc (dhcp_hostname, len); + if (tmp != NULL) { + dhcp_hostname = tmp; + strncpy (dhcp_hostname, p, len); + } else { /* realloc failed */ + printf ("dhcpc: realloc failed (%s:%d)", __FILE__, __LINE__); + free (dhcp_hostname, 0); + dhcp_hostname = NULL; + } + } else { /* dhcp_hostname == NULL */ + dhcp_hostname = strndup (p, len); + } + break; + + case 7: + /* Log servers */ + if (len % 4) { + printf ("dhcpc: Log server Len is %d\n", len); + continue; + } + if (len > 0) + { + memcpy (&rtems_bsdnet_log_host_address, p, 4); + dhcp_gotlogserver = 1; + } + break; + + case 15: + /* Domain name */ + if (p[0]) + { + rtems_bsdnet_domain_name = strdup (p); + } + break; + + case 16: /* Swap server IP address. unused */ + break; + + case 50: + /* DHCP Requested IP Address */ + if (len != 4) + printf ("dhcpc: DHCP option requested IP len is %d", len); + /* + * although nothing happens here, this case keeps the client + * from complaining about unknown options. The Requested IP + * is necessary to return to the server for a DHCP REQUEST + */ + break; + + case 51: + /* DHCP Lease Length */ + if (len != 4) { + printf ("dhcpc: DHCP option lease-length len is %d", len); + continue; + } + memcpy (&dhcp_lease_time, &p[0], 4); + dhcp_lease_time = ntohl (dhcp_lease_time); + break; + + case 52: + /* DHCP option override */ + if (len != 1) { + printf ("dhcpc: DHCP option overload len is %d", len); + continue; + } + dhcp_option_overload = p[0]; + break; + + case 53: + /* DHCP message */ + if (len != 1) { + printf ("dhcpc: DHCP message len is %d", len); + continue; + } + dhcp_message_type = p[0]; + break; + + case 128: /* Site-specific option for DHCP servers that + * a) don't supply tag 54 + * and + * b) don't supply the server address in siaddr + * For example, on Solaris 2.6 in.dhcpd, include in the dhcptab: + * Bootsrv s Site,128,IP,1,1 + * and use that symbol in the macro that defines the client: + * Bootsrv= + */ + case 54: + /* DHCP server */ + if (len != 4) { + printf ("dhcpc: DHCP server len is %d", len); + continue; + } + memcpy (&rtems_bsdnet_bootp_server_address, p, 4); + dhcp_gotserver = 1; + break; + + case 66: + /* DHCP server name option */ + if (p[0]) + rtems_bsdnet_bootp_server_name = strdup (p); + break; + + case 67: + /* DHCP bootfile option */ + if (p[0]) + rtems_bsdnet_bootp_boot_file_name = strdup (p); + break; + + default: + break; + } + } +} + +/* + * Generate the packet for a DHCP DISCOVER. + */ +static int +dhcp_discover_req (struct dhcp_packet* call, + struct sockaddr_dl *sdl, + unsigned long *xid) +{ + int len = 0; + + memset (call, 0, sizeof (struct dhcp_packet)); + + /* + * Send a DHCP DISCOVER Message + */ + call->op = DHCP_BOOTREQUEST; + call->htype = 1; /* 10mb ethernet */ + call->hlen = sdl->sdl_alen; /* Hardware address length */ + call->hops = 0; + (*xid)++; + call->xid = htonl (*xid); + call->flags = htons (DHCP_BROADCAST); + + memcpy (&call->chaddr, LLADDR (sdl), sdl->sdl_alen); + + /* + * Magic cookie. + */ + memcpy (&call->vend[len], dhcp_magic_cookie, sizeof (dhcp_magic_cookie)); + len += sizeof (dhcp_magic_cookie); + + /* + * DHCP Message type. + */ + call->vend[len++] = DHCP_MESSAGE; + call->vend[len++] = 1; + call->vend[len++] = DHCP_DISCOVER; + + /* + * If a host name is set add it to the request. + */ + if (rtems_bsdnet_config.hostname && \ + (strlen (rtems_bsdnet_config.hostname) > 1) && + (strlen (rtems_bsdnet_config.hostname) < 32)) { + call->vend[len++] = DHCP_HOST; + call->vend[len++] = strlen (rtems_bsdnet_config.hostname); + memcpy (&call->vend[len], + rtems_bsdnet_config.hostname, + strlen (rtems_bsdnet_config.hostname)); + len += strlen (rtems_bsdnet_config.hostname); + } + + /* + * DHCP Parameter request list + */ + call->vend[len++] = DHCP_PARAMETERS; + call->vend[len++] = sizeof (dhcp_request_parameters); + memcpy (&call->vend[len], &dhcp_request_parameters, sizeof (dhcp_request_parameters)); + len += sizeof (dhcp_request_parameters); + + /* + * Lease time. + */ + call->vend[len++] = DHCP_LEASE; + call->vend[len++] = 4; + memset (&call->vend[len], 0xFF, 4); /* request infinite lease time */ + len += 4; + + /* + * End. + */ + call->vend[len++] = DHCP_OPTION_END; + call->secs = 0; + + return len; +} + +/* + * Generate the packet for a DHCP REQUEST. + */ +static int +dhcp_request_req (struct dhcp_packet* call, + struct dhcp_packet* reply, + struct sockaddr_dl *sdl, + int broadcast) +{ + int len = 0; + unsigned long temp; + char *hostname; + + memset (call, 0, sizeof (struct dhcp_packet)); + + /* + * Send a DHCP REQUEST Message + */ + call->op = DHCP_BOOTREQUEST; + call->htype = 1; /* 10mb ethernet */ + call->hlen = sdl->sdl_alen; /* Hardware address length */ + call->hops = 0; + call->xid = reply->xid; + if (broadcast) + call->flags = htons (DHCP_BROADCAST); + else + { + call->flags = htons (DHCP_UNICAST); + call->ciaddr = reply->yiaddr; + } + memcpy (&call->chaddr, LLADDR (sdl), sdl->sdl_alen); + + /* + * Magic cookie. + */ + memcpy (&call->vend[len], dhcp_magic_cookie, sizeof (dhcp_magic_cookie)); + len += sizeof (dhcp_magic_cookie); + + /* + * DHCP Message type. + */ + call->vend[len++] = DHCP_MESSAGE; + call->vend[len++] = 1; + call->vend[len++] = DHCP_REQUEST; + + /* + * DHCP server + */ + if (broadcast) + { + call->vend[len++] = DHCP_SERVER; + call->vend[len++] = sizeof (rtems_bsdnet_bootp_server_address); + memcpy (&call->vend[len], &rtems_bsdnet_bootp_server_address, + sizeof (rtems_bsdnet_bootp_server_address)); + len += sizeof (rtems_bsdnet_bootp_server_address); + } + + /* + * Requested IP + */ + call->vend[len++] = DHCP_REQUESTED_IP; + call->vend[len++] = sizeof (reply->yiaddr); + memcpy (&call->vend[len], &reply->yiaddr, sizeof (reply->yiaddr)); + len += sizeof (reply->yiaddr); + + /* + * DHCP Parameter request list + */ + call->vend[len++] = DHCP_PARAMETERS; + call->vend[len++] = sizeof (dhcp_request_parameters); + memcpy (&call->vend[len], &dhcp_request_parameters, sizeof (dhcp_request_parameters)); + len += sizeof (dhcp_request_parameters); + + /* + * Lease time. + * For the REQUEST, return the lease time the server offered. + */ + call->vend[len++] = DHCP_LEASE; + call->vend[len++] = 4; + temp = htonl (dhcp_lease_time); + memcpy (&call->vend[len], &temp, sizeof (unsigned long)); + len += 4; + + /* + * Host name. + */ + hostname = malloc (MAXHOSTNAMELEN, 0, M_NOWAIT); + if (hostname != NULL) + { + if (gethostname (hostname, MAXHOSTNAMELEN) == 0) + { + call->vend[len++] = DHCP_HOST; + call->vend[len++] = strlen (hostname); + strcpy ((char*) &call->vend[len], hostname); + len += strlen (hostname); + } + free (hostname, 0); + } + + /* + * End. + */ + call->vend[len++] = DHCP_OPTION_END; + call->secs = 0; + + return len; +} + +/* + * Variables for the DHCP task. + */ +static struct dhcp_packet dhcp_req; +static rtems_id dhcp_task_id; + +/* + * The DHCP task counts until half the lease time has expired. + * When this period is up, it sends a DHCP REQUEST packet to the + * server again to renew the lease. + * If the lease is renewed, the task starts counting again. + * If the lease is not renewed, the task retries until it is. + * + * The task will not rebind if the lease is not renewed. + */ +static void +dhcp_task (rtems_task_argument _sdl) +{ + unsigned long count; + struct dhcp_packet call; + struct sockaddr_dl *sdl; + rtems_event_set event_out; + unsigned int timeout = 0; + int error; + struct proc *procp = NULL; + rtems_status_code ev_st; + + sdl = (struct sockaddr_dl *) _sdl; + + count = dhcp_elapsed_time; + + while (true) + { + /* + * Sleep until the next poll + */ + timeout = RTEMS_MILLISECONDS_TO_TICKS (1000); + ev_st = rtems_event_receive (RTEMS_EVENT_0, + RTEMS_WAIT | RTEMS_EVENT_ANY, + timeout, &event_out); + + /* + * Check if not a poll timeout. So when ANY event received, exit task. + * Actually, only event RTEMS_EVENT_0 sent from rtem_dhcp_failsafe.c + * if "failsafe" dhcp enabled when interface down. Otherwise, no + * event should occur, just timeout. + */ + if(ev_st != RTEMS_TIMEOUT) + break; + + count++; + + if (count >= (dhcp_lease_time / 2)) + { + rtems_bsdnet_semaphore_obtain (); + + dhcp_request_req (&call, &dhcp_req, sdl, true); + + /* + * Send the Request. + */ + error = bootpc_call ((struct bootp_packet *)&call, + (struct bootp_packet *)&dhcp_req, procp, NULL, 0); + if (error) { + rtems_bsdnet_semaphore_release (); + printf ("DHCP call failed -- error %d", error); + continue; + } + + /* + * Check for DHCP ACK/NACK + */ + if (memcmp (&dhcp_req.vend[0], + dhcp_magic_cookie, + sizeof (dhcp_magic_cookie)) != 0) + { + rtems_bsdnet_semaphore_release (); + printf ("DHCP server did not send Magic Cookie.\n"); + continue; + } + + /* + * We have an ack. Clear the DNS entries that have been assigned by a previous + * DHCP request. + */ + clean_dns_entries (); + + /* + * Process this requests options. + */ + process_options (&dhcp_req.vend[4], sizeof (dhcp_req.vend) - 4); + + if (dhcp_message_type != DHCP_ACK) + { + rtems_bsdnet_semaphore_release (); + printf ("DHCP server did not accept the DHCP request"); + continue; + } + + rtems_bsdnet_semaphore_release (); + + count = 0; + } + } + + + dhcp_task_id = 0; + printf ("dhcpc: exiting lease renewal task.\n"); + rtems_task_exit(); + +} + +/* + * Start the DHCP task. + */ +static rtems_status_code +dhcp_start_task (struct sockaddr_dl *sdl, + struct dhcp_packet *reply, + int priority) +{ + rtems_status_code sc; + + memcpy (&dhcp_req, reply, sizeof (struct dhcp_packet)); + + sc = rtems_task_create (rtems_build_name ('d','h','c','p'), + priority, + 2048, + RTEMS_PREEMPT | + RTEMS_NO_TIMESLICE | + RTEMS_NO_ASR | + RTEMS_INTERRUPT_LEVEL (0), + RTEMS_LOCAL, + &dhcp_task_id); + + if (sc != RTEMS_SUCCESSFUL) + return sc; + + sc = rtems_task_start (dhcp_task_id, + dhcp_task, + (rtems_task_argument) sdl); + + if (sc != RTEMS_SUCCESSFUL) + return sc; + + return RTEMS_SUCCESSFUL; +} + +/* + * Check if the chosen interface already has an IP. + */ +static int +dhcp_interface_has_ip (struct ifreq *ireq, struct socket *so, struct proc *procp) +{ + struct sockaddr_in* sin; + int error; + + /* + * Check if the interface is already up. + */ + error = ifioctl(so, SIOCGIFFLAGS, (caddr_t)ireq, procp); + if (error) + return 0; + + if ((ireq->ifr_flags & IFF_UP) == 0) + return 0; + + sin = (struct sockaddr_in *)&ireq->ifr_addr; + bzero ((caddr_t)sin, sizeof (struct sockaddr_in)); + sin->sin_len = sizeof (struct sockaddr_in); + sin->sin_family = AF_INET; + error = ifioctl (so, SIOCGIFADDR, (caddr_t)ireq, procp); + if (error) + return 0; + + if (sin->sin_addr.s_addr != 0) + return 1; + + return 0; +} + +/* + * DCHP Client Routine + * - The first DHCP offer is always accepted + * - No DHCP DECLINE message is sent if ARPing fails + * + * return value: + * 0: ok + * < 0: failed to startup or configure interface + */ +static int +dhcp_init (int update_files) +{ + struct dhcp_packet call; + struct dhcp_packet reply; + static unsigned long xid = ~0xFF; + struct ifreq ireq; + struct ifnet *ifp; + struct socket *so; + int error; + struct sockaddr_in myaddr; + struct ifaddr *ifa; + struct sockaddr_dl *sdl = NULL; + struct proc *procp = NULL; + char expected_dhcp_payload[7]; + + clean_dns_entries(); + + /* + * If we are to update the files create the root + * file structure. + */ + if (update_files) + if (rtems_create_root_fs () < 0) { + printf("Error creating the root filesystem.\nFile not created.\n"); + update_files = 0; + } + + /* + * Find a network interface. + */ + for (ifp = ifnet; ifp != 0; ifp = ifp->if_next) + if ((ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) == 0) + break; + if (ifp == NULL){ + printf ("dhcpc_init: no suitable interface\n"); + return -1; + } + + memset (&ireq, 0, sizeof (ireq)); + sprintf (ireq.ifr_name, "%s%d", ifp->if_name, ifp->if_unit); + + if ((error = socreate (AF_INET, &so, SOCK_DGRAM, 0, procp)) != 0) { + printf ("dhcpc_init: socreate, error: %s\n", strerror(error)); + return -1; + } + + if (!dhcp_interface_has_ip (&ireq, so, procp)) + bootpc_fakeup_interface (&ireq, so, procp); + + /* + * Get HW address + */ + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) + if (ifa->ifa_addr->sa_family == AF_LINK && + (sdl = ((struct sockaddr_dl *) ifa->ifa_addr)) && + sdl->sdl_type == IFT_ETHER) + break; + + if (!sdl){ + printf ("dhcpc_init: Unable to find HW address\n"); + soclose (so); + return -1; + } + if (sdl->sdl_alen != EALEN) { + printf ("dhcpc_init: HW address len is %d, expected value is %d\n", + sdl->sdl_alen, EALEN); + soclose (so); + return -1; + } + + + /* + * Build the DHCP Discover + */ + dhcp_discover_req (&call, sdl, &xid); + + /* + * Expect a DHCP offer as response to DHCP discover + */ + memcpy(expected_dhcp_payload, dhcp_magic_cookie, sizeof(dhcp_magic_cookie)); + expected_dhcp_payload[sizeof(dhcp_magic_cookie) ]=0x35; /* DHCP */ + expected_dhcp_payload[sizeof(dhcp_magic_cookie)+1]=0x01; /* Length : 1 */ + expected_dhcp_payload[sizeof(dhcp_magic_cookie)+2]=0x02; /* DHCP_OFFER */ + + /* + * Send the Discover. + */ + error = bootpc_call ((struct bootp_packet *)&call, + (struct bootp_packet *)&reply, procp, + expected_dhcp_payload, sizeof(expected_dhcp_payload)); + if (error) { + printf ("BOOTP call failed -- %s\n", strerror(error)); + soclose (so); + return -1; + } + + /* + * Check for DHCP OFFER + */ + if (memcmp (&reply.vend[0], dhcp_magic_cookie, sizeof (dhcp_magic_cookie)) != 0) { + printf ("DHCP server did not send Magic Cookie.\n"); + soclose (so); + return -1; + } + + process_options (&reply.vend[4], sizeof (reply.vend) - 4); + + if (dhcp_message_type != DHCP_OFFER) { + printf ("DHCP server did not send a DHCP Offer.\n"); + soclose (so); + return -1; + } + + /* + * Expect a DHCP_ACK as response to the DHCP REQUEST + * No need to reinitialize the whole expected_dhcp_payload variable, + * header and first two bytes of the payload are filled from DHCP offer + */ + expected_dhcp_payload[sizeof(dhcp_magic_cookie)+2]=0x05; /* DHCP_ACK */ + + /* + * Send a DHCP REQUEST + */ + dhcp_request_req (&call, &reply, sdl, true); + + error = bootpc_call ((struct bootp_packet *)&call, + (struct bootp_packet *)&reply, procp, + expected_dhcp_payload, sizeof(expected_dhcp_payload)); + if (error) { + printf ("BOOTP call failed -- %s\n", strerror(error)); + soclose (so); + return -1; + } + + /* + * Check for DHCP ACK/NACK + */ + if (memcmp (&reply.vend[0], dhcp_magic_cookie, sizeof (dhcp_magic_cookie)) != 0) { + printf ("DHCP server did not send Magic Cookie.\n"); + soclose (so); + return -1; + } + + process_options (&reply.vend[4], sizeof (reply.vend) - 4); + + if (dhcp_message_type != DHCP_ACK) { + printf ("DHCP server did not accept the DHCP request\n"); + soclose (so); + return -1; + } + + /* + * Initialize network address structures + */ + memset (&myaddr, 0, sizeof (myaddr)); + memset (&dhcp_netmask, 0, sizeof (dhcp_netmask)); + memset (&dhcp_gw, 0, sizeof (dhcp_gw)); + myaddr.sin_len = sizeof (myaddr); + myaddr.sin_family = AF_INET; + dhcp_netmask.sin_len = sizeof (dhcp_netmask); + dhcp_netmask.sin_family = AF_INET; + dhcp_gw.sin_len = sizeof (dhcp_gw); + dhcp_gw.sin_family = AF_INET; + + /* + * Set our address + */ + myaddr.sin_addr = reply.yiaddr; + + /* + * Process BOOTP/DHCP options + */ + if (memcmp (&reply.vend[0], dhcp_magic_cookie, sizeof (dhcp_magic_cookie)) == 0) + process_options (&reply.vend[4], sizeof (reply.vend) - 4); + + if (dhcp_option_overload & 1) + process_options ((unsigned char*) reply.file, sizeof reply.file); + else + if (reply.file[0]) + rtems_bsdnet_bootp_boot_file_name = strdup (reply.file); + + if (dhcp_option_overload & 2) + process_options ((unsigned char*) reply.sname, sizeof reply.sname); + else + if (reply.sname[0]) + rtems_bsdnet_bootp_server_name = strdup (reply.sname); + + /* + * Use defaults if values were not supplied by BOOTP/DHCP options + */ + if (!dhcp_gotnetmask) + { + if (IN_CLASSA (ntohl (myaddr.sin_addr.s_addr))) + dhcp_netmask.sin_addr.s_addr = htonl (IN_CLASSA_NET); + else if (IN_CLASSB (ntohl (myaddr.sin_addr.s_addr))) + dhcp_netmask.sin_addr.s_addr = htonl (IN_CLASSB_NET); + else + dhcp_netmask.sin_addr.s_addr = htonl (IN_CLASSC_NET); + } + + if (!dhcp_gotserver) + rtems_bsdnet_bootp_server_address = reply.siaddr; + + if (!dhcp_gotgw) + dhcp_gw.sin_addr = reply.giaddr; + + if (!dhcp_gotlogserver) + rtems_bsdnet_log_host_address = rtems_bsdnet_bootp_server_address; + + printsetup (ifp->if_name, myaddr.sin_addr, dhcp_netmask.sin_addr, + rtems_bsdnet_bootp_server_address, dhcp_gw.sin_addr); + + /* + * Update the files if we are asked too. + */ + if (update_files) { + char *dn = rtems_bsdnet_domain_name; + char *hn = dhcp_hostname; + if (!dn) + dn = "mydomain"; + if (!hn) + { + hn = "me"; + sethostname (hn, strlen (hn)); + } + rtems_rootfs_append_host_rec(myaddr.sin_addr.s_addr, hn, dn); + + /* + * Should the given domainname be used here ? + */ + if (dhcp_gotserver) { + if (rtems_bsdnet_bootp_server_name) + hn = rtems_bsdnet_bootp_server_name; + else + hn = "bootps"; + rtems_rootfs_append_host_rec(rtems_bsdnet_bootp_server_address.s_addr, + hn, dn); + } + + if (dhcp_gotlogserver) { + rtems_rootfs_append_host_rec(rtems_bsdnet_log_host_address.s_addr, + "logs", dn); + } + + /* + * Setup the DNS configuration file /etc/resolv.conf. + */ + if (rtems_bsdnet_nameserver_count) { + int i; + char buf[64]; + const char *bufl[1]; + + bufl[0] = buf; + +#define MKFILE_MODE (S_IRUSR | S_IWUSR | S_IWGRP | S_IRGRP | S_IROTH) + + if (rtems_bsdnet_domain_name && + (strlen(rtems_bsdnet_domain_name) < (sizeof(buf) - 1))) { + strcpy(buf, "search "); + strcat(buf, rtems_bsdnet_domain_name); + strcat(buf, "\n"); + rtems_rootfs_file_append ("/etc/resolv.conf", MKFILE_MODE, 1, bufl); + } + + for (i = 0; i < rtems_bsdnet_nameserver_count; i++) { + char addrbuf[INET_ADDRSTRLEN]; + strcpy(buf, "nameserver "); + strcat(buf, inet_ntoa_r(rtems_bsdnet_ntpserver[i], addrbuf)); + strcat(buf, "\n"); + if (rtems_rootfs_file_append ("/etc/resolv.conf", MKFILE_MODE, 1, bufl)) + break; + } + } + } + + /* + * Configure the interface with the new settings + */ + error = bootpc_adjust_interface (&ireq, so, + &myaddr, &dhcp_netmask, &dhcp_gw, procp); + + /* + * Start the DHCP task if the lease isn't infinite. + */ + if (dhcp_lease_time != 0xffffffff) + dhcp_start_task (sdl, &reply, 150); + + soclose (so); + + return 0; +} + +/* + * + * RTEMS Entry point to DHCP client + * + */ +void rtems_bsdnet_do_dhcp (void) +{ + bool update = true; + rtems_bsdnet_semaphore_obtain (); + while( dhcp_init (update) < 0 ) { + update = false; + rtems_bsdnet_semaphore_release(); + rtems_task_wake_after(RTEMS_MILLISECONDS_TO_TICKS(1000)); + rtems_bsdnet_semaphore_obtain (); + } + rtems_bsdnet_semaphore_release (); +} + +int rtems_bsdnet_do_dhcp_timeout( void ) +{ + int return_value; + + rtems_bsdnet_semaphore_obtain (); + return_value = dhcp_init (false); + rtems_bsdnet_semaphore_release (); + + return return_value; +} + +void rtems_bsdnet_dhcp_down (void) +{ + if(dhcp_task_id != 0) { + rtems_event_send (dhcp_task_id, RTEMS_EVENT_0); + } +} + +void +rtems_bsdnet_do_dhcp_refresh_only (unsigned long xid, + unsigned long lease_time, + unsigned long elapsed_time, + unsigned long ip_address, + unsigned long srv_address, + const char* hostname) +{ + struct dhcp_packet reply; + struct ifnet *ifp = NULL; + struct ifaddr *ifa = NULL; + struct sockaddr_dl *sdl = NULL; + struct sockaddr_in *sin = NULL; + int match = 0; + struct ifnet *mtif = NULL; + + /* + * If an infinite lease has been granted, no task is needed. + */ + if (lease_time == 0xffffffff) + return; + + /* + * Find a network interface. + */ + for (ifp = ifnet; (ifp != NULL) && !match; ifp = ifp->if_next) + if ((ifp->if_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) == 0) + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) + if (ifa->ifa_addr->sa_family == AF_INET) + { + sin = (struct sockaddr_in *) ifa->ifa_addr; + if (sin->sin_addr.s_addr == htonl (ip_address)) + { + mtif = ifp; + match = 1; + break; + } + } + + if (!match) { + printf ("dhcpc: no matching interface\n"); + return; + } + + for (ifa = mtif->if_addrlist; ifa != NULL; ifa = ifa->ifa_next) + if (ifa->ifa_addr->sa_family == AF_LINK && + (sdl = ((struct sockaddr_dl *) ifa->ifa_addr)) && + sdl->sdl_type == IFT_ETHER) + break; + + if (!match) { + printf ("dhcpc: no matching interface address\n"); + return; + } + + /* + * Set up given values in a simulated DHCP reply. + */ + memset (&reply, 0x00, sizeof (reply)); + reply.xid = htonl (xid); + reply.yiaddr.s_addr = htonl (ip_address); + reply.siaddr.s_addr = htonl (srv_address); + if (reply.siaddr.s_addr != rtems_bsdnet_bootp_server_address.s_addr) + { + memcpy (&rtems_bsdnet_bootp_server_address, &reply.siaddr, + sizeof (reply.siaddr)); + } + + dhcp_lease_time = lease_time; + dhcp_elapsed_time = elapsed_time; + + if (hostname) + { + sethostname ((char *) hostname, strlen (hostname)); + dhcp_hostname = bootp_strdup_realloc (dhcp_hostname, hostname); + } + + dhcp_start_task (sdl, &reply, 150); +} diff --git a/rtems/rtems_dhcp_failsafe.c b/rtems/rtems_dhcp_failsafe.c new file mode 100644 index 0000000..65c107d --- /dev/null +++ b/rtems/rtems_dhcp_failsafe.c @@ -0,0 +1,380 @@ +#include + +/* + Description: Wrapper around DHCP client to restart it when the interface + moves to another network. + + Authors: Arnout Vandecappelle , Essensium/Mind + Maarten Van Es , Essensium/Mind + (C) Septentrio 2008 + + The license and distribution terms for this file may be + found in the file LICENSE in this distribution or at + http://www.rtems.org/license/LICENSE. + + + To use this functionality, call rtems_bsdnet_do_dhcp_failsafe() or set it + as the bootp member of the rtems_bsdnet_config structure. + + The rtems_bsdnet_do_dhcp_failsafe() function provides the following + functionalities: + + * It starts DHCP on the first non-loopback, non-point-to-point interface. + Before DHCP is started, any existing IP address on that interface is + removed, as well as the default route. + + * If DHCP fails to acquire an address for whatever reason, the interface + is reconfigured with the static address provided in its + rtems_bsdnet_ifconfig structure, and the default route from + rtems_bsdnet_config is restored. + It is possible to change the address in the rtems_bsdnet_ifconfig structure + while the system is running. + + * Optionally, after the interface is configured (either with DHCP or + statically), a task is started to monitor it. When the interface remains + disconnected (i.e. its IFF_RUNNING flag is off) for network_fail_timeout + seconds, the dhcp lease renewal is stopped. As soon as the interface is + connected again, DHCP is started again as above. + If network_fail_timeout is set to 0, the monitor task is not started. + + * Optionally, when the interface is disconnected, it is also brought down + for network_down_time seconds. Since this possibly makes the IFF_RUNNING + flag unuseable, the interface is brought up again before the flag is + polled. + If network_down_time is set to 0, the interface is not brought down. + + * Optionally, before DHCP is started, we wait for broadcast_delay seconds. + This is necessary to allow some routers to perform spanning tree discovery. + + Note that DHCP doesn't work well with multiple interfaces: only one of them + is configured using DHCP, and we can't guarantee it's the same one that is + monitored by this function. + +*/ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +struct proc; /* Unused parameter of some functions. */ +#include +#include +#include +#include /* for sockaddr_in */ +#include /* for if.h */ +#include /* for in_var.h */ +#include /* for in_aliasreq */ +#include /* for ioctl definitions */ + +static int network_fail_timeout = RTEMS_DHCP_FAILSAFE_NETWORK_FAIL_TIMEOUT; +static int network_down_time = RTEMS_DHCP_FAILSAFE_NETWORK_DOWN_TIME; +static int broadcast_delay = RTEMS_DHCP_FAILSAFE_BROADCAST_DELAY; +static int dhcp_monitor_priority = RTEMS_DHCP_FAILSAFE_DHCP_MONITOR_PRIORITY; + +void rtems_bsdnet_dhcp_failsafe_config( + int network_fail_timeout_, + int network_down_time_, + int broadcast_delay_, + int dhcp_monitor_priority_ +) +{ + network_fail_timeout = network_fail_timeout_; + network_down_time = network_down_time_; + broadcast_delay = broadcast_delay_; + dhcp_monitor_priority = dhcp_monitor_priority_; +} + +/* + * returns 0 when successful, negative value for failure + */ +static int remove_address(const char *if_name) +{ + struct sockaddr_in address; + struct in_aliasreq ifra; + int retval = 0; + + memset (&address, '\0', sizeof (address)); + address.sin_len = sizeof (address); + address.sin_family = AF_INET; + address.sin_addr.s_addr = INADDR_ANY; + + /* Remove old default route to 0.0.0.0 */ + if (rtems_bsdnet_rtrequest (RTM_DELETE, (struct sockaddr *)&address, NULL, + (struct sockaddr *)&address, + (RTF_UP | RTF_STATIC), NULL) < 0 ) { + printf ("Failed to delete default route: %s (%d)\n", strerror (errno), errno); + retval = -1; + } + + /* Remove old ip-address */ + if (rtems_bsdnet_ifconfig(if_name, SIOCGIFADDR, &address) < 0) { + printf ("Failed to get if address: %s (%d)\n", strerror (errno), errno); + return -1; + } + + strncpy (ifra.ifra_name, if_name, IFNAMSIZ); + memcpy (&ifra.ifra_addr, &address, sizeof(address)); + if (rtems_bsdnet_ifconfig(if_name, SIOCDIFADDR, &ifra)) { + printf ("Can't delete if address: %s (%d)\n", strerror (errno), errno); + return -1; + } + + return retval; +} + + +static int +dhcp_if_down (const char* ifname) +{ + int16_t flags; + if (rtems_bsdnet_ifconfig (ifname, SIOCGIFFLAGS, &flags) < 0) { + printf ("Can't get flags for %s: %s\n", ifname, strerror (errno)); + return -1; + } + if (flags & IFF_UP) { + flags &= ~IFF_UP; + if (rtems_bsdnet_ifconfig (ifname, SIOCSIFFLAGS, &flags) < 0) { + printf ("Can't bring %s down: %s\n", ifname, strerror (errno)); + return -1; + } + } + + return 0; +} + +static int +dhcp_if_up (const char* ifname) +{ + int16_t flags; + if (rtems_bsdnet_ifconfig (ifname, SIOCGIFFLAGS, &flags) < 0) { + printf ("Can't get flags for %s: %s\n", ifname, strerror (errno)); + return -1; + } + if (!(flags & IFF_UP)) { + flags |= IFF_UP; + if (rtems_bsdnet_ifconfig (ifname, SIOCSIFFLAGS, &flags) < 0) { + printf ("Can't bring %s up: %s\n", ifname, strerror (errno)); + return -1; + } + } + + return 0; +} + + +static int +set_static_address (struct rtems_bsdnet_ifconfig *ifp) +{ + int16_t flags; + struct sockaddr_in address; + struct sockaddr_in netmask; + struct sockaddr_in broadcast; + struct sockaddr_in gateway; + + if (ifp->ip_address != NULL) { + printf("Setting static address for interface %s.\n", ifp->name); + + /* + * Bring interface up + */ + if (dhcp_if_up (ifp->name) < 0) + return -1; + + /* + * Set interface netmask + */ + memset (&netmask, '\0', sizeof netmask); + netmask.sin_len = sizeof netmask; + netmask.sin_family = AF_INET; + netmask.sin_addr.s_addr = inet_addr (ifp->ip_netmask); + if (rtems_bsdnet_ifconfig (ifp->name, SIOCSIFNETMASK, &netmask) < 0) { + printf ("Can't set %s netmask: %s\n", ifp->name, strerror (errno)); + return -1; + } + + /* + * Set interface address + */ + memset (&address, '\0', sizeof address); + address.sin_len = sizeof address; + address.sin_family = AF_INET; + address.sin_addr.s_addr = inet_addr (ifp->ip_address); + if (rtems_bsdnet_ifconfig (ifp->name, SIOCSIFADDR, &address) < 0) { + printf ("Can't set %s address: %s\n", ifp->name, strerror (errno)); + return -1; + } + + /* + * Set interface broadcast address if the interface has the + * broadcast flag set. + */ + if (rtems_bsdnet_ifconfig (ifp->name, SIOCGIFFLAGS, &flags) < 0) { + printf ("Can't read %s flags: %s\n", ifp->name, strerror (errno)); + return -1; + } + if (flags & IFF_BROADCAST) { + memset (&broadcast, '\0', sizeof broadcast); + broadcast.sin_len = sizeof broadcast; + broadcast.sin_family = AF_INET; + broadcast.sin_addr.s_addr = address.sin_addr.s_addr | ~netmask.sin_addr.s_addr; + + if (rtems_bsdnet_ifconfig (ifp->name, SIOCSIFBRDADDR, &broadcast) < 0) { + struct in_addr in_addr; + char buf[20]; + + in_addr.s_addr = broadcast.sin_addr.s_addr; + if (!inet_ntop (AF_INET, &in_addr, buf, sizeof (buf))) + strcpy (buf, "?.?.?.?"); + printf ("Can't set %s broadcast address %s: %s\n", ifp->name, buf, strerror (errno)); + } + } + } + + /* + * Set default route + */ + if (rtems_bsdnet_config.gateway) { + address.sin_addr.s_addr = INADDR_ANY; + netmask.sin_addr.s_addr = INADDR_ANY; + memset (&gateway, '\0', sizeof gateway); + gateway.sin_len = sizeof gateway; + gateway.sin_family = AF_INET; + gateway.sin_addr.s_addr = inet_addr (rtems_bsdnet_config.gateway); + + if (rtems_bsdnet_rtrequest (RTM_ADD, + (struct sockaddr *) &address, + (struct sockaddr *) &gateway, + (struct sockaddr *) &netmask, + (RTF_UP | RTF_GATEWAY | RTF_STATIC), + NULL + ) < 0) { + printf ("Can't set default route: %s\n", strerror (errno)); + return -1; + } + } + + return 0; +} + +static void +do_dhcp_init (struct rtems_bsdnet_ifconfig *ifp) +{ + if (broadcast_delay) { + /* Wait before sending broadcast. */ + rtems_task_wake_after(RTEMS_MILLISECONDS_TO_TICKS(broadcast_delay * 1000)); + } + + printf ("starting dhcp client...\n"); + + remove_address(ifp->name); + if (rtems_bsdnet_do_dhcp_timeout () != 0) { + remove_address(ifp->name); + set_static_address (ifp); /* use static ip-address if dhcp failed */ + } + +} + +/* + * Main dhcp monitor thread + */ +static void dhcp_monitor_task (rtems_task_argument ifp_arg) +{ + struct rtems_bsdnet_ifconfig *ifp = (struct rtems_bsdnet_ifconfig *)ifp_arg; + char *ifname = ifp->name; + unsigned int downcount = 0; + int16_t ifflags; + int must_renew = FALSE; + + while (TRUE) { + if (rtems_bsdnet_ifconfig(ifname, SIOCGIFFLAGS, &ifflags) < 0) { + printf ("Failed to get if flags: %s (%d)\n", strerror (errno), errno); + goto error_out; + } + + if ((ifflags & IFF_RUNNING) != 0) { + if(must_renew) { + must_renew = FALSE; + do_dhcp_init(ifp); + } + downcount = 0; + } else { + if (downcount < network_fail_timeout) { + downcount++; + + if (downcount == network_fail_timeout) { + printf ("lost network connection...\n"); + rtems_bsdnet_dhcp_down (); + must_renew = TRUE; + dhcp_if_down(ifname); + rtems_task_wake_after(RTEMS_MILLISECONDS_TO_TICKS(network_down_time * 1000)); + dhcp_if_up(ifname); + downcount = 0; + } + } + } + + rtems_task_wake_after(RTEMS_MILLISECONDS_TO_TICKS(1000)); + } + +error_out: + printf("Stopping dhcp monitoring application.\n"); + rtems_task_exit(); +} + +/* +* initialize dhcp monitoring application +* start dhcp monitoring thread +*/ +void rtems_bsdnet_do_dhcp_failsafe (void) +{ + rtems_status_code sc; + rtems_id id; + struct rtems_bsdnet_ifconfig *ifp; + int16_t ifflags; + + /* Find a suitable interface */ + for (ifp = rtems_bsdnet_config.ifconfig; ifp; ifp = ifp->next) { + if (rtems_bsdnet_ifconfig (ifp->name, SIOCGIFFLAGS, &ifflags) < 0) + continue; + if ((ifflags & (IFF_LOOPBACK | IFF_POINTOPOINT)) == 0) + break; + } + if (ifp == NULL){ + printf ("dhcpc_failsafe: no suitable interface\n"); + return; + } + printf("starting dhcp on interface %s\n", ifp->name); + do_dhcp_init(ifp); + + if (network_fail_timeout) { + sc = rtems_task_create (rtems_build_name ('d','h','c','m'), + dhcp_monitor_priority, + 2048, + RTEMS_PREEMPT | + RTEMS_NO_TIMESLICE | + RTEMS_NO_ASR | + RTEMS_INTERRUPT_LEVEL (0), + RTEMS_LOCAL, + &id); + + if (sc != RTEMS_SUCCESSFUL) { + printf ("Failed to create dhcp monitor task, code %d\n", sc); + return; + } + + sc = rtems_task_start (id, dhcp_monitor_task, (rtems_task_argument) ifp); + + if (sc != RTEMS_SUCCESSFUL) { + printf ("Failed to start dhcp monitor task, code %d\n", sc); + } + } +} + diff --git a/rtems/rtems_dhcp_failsafe.h b/rtems/rtems_dhcp_failsafe.h new file mode 100644 index 0000000..3c5a59f --- /dev/null +++ b/rtems/rtems_dhcp_failsafe.h @@ -0,0 +1,64 @@ +/* + Description: Wrapper around DHCP client to restart it when the interface + moves to another network. + + Authors: Arnout Vandecappelle , Essensium/Mind + Maarten Van Es , Essensium/Mind + (C) Septentrio 2008 + + The license and distribution terms for this file may be + found in the file LICENSE in this distribution or at + http://www.rtems.org/license/LICENSE. +*/ + +#ifndef _RTEMS_DHCP_FAILSAFE_H_ +#define _RTEMS_DHCP_FAILSAFE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Default settings for the DHCP failsafe. They can be overridden + * using rtems_bsdnet_dhcp_failsafe_config(); see that for descriptions. + */ +#ifndef RTEMS_DHCP_FAILSAFE_NETWORK_FAIL_TIMEOUT +#define RTEMS_DHCP_FAILSAFE_NETWORK_FAIL_TIMEOUT 5 +#endif + +#ifndef RTEMS_DHCP_FAILSAFE_NETWORK_DOWN_TIME +#define RTEMS_DHCP_FAILSAFE_NETWORK_DOWN_TIME 30 +#endif + +#ifndef RTEMS_DHCP_FAILSAFE_BROADCAST_DELAY +#define RTEMS_DHCP_FAILSAFE_BROADCAST_DELAY 0 +#endif + +#ifndef RTEMS_DHCP_FAILSAFE_DHCP_MONITOR_PRIORITY +#define RTEMS_DHCP_FAILSAFE_DHCP_MONITOR_PRIORITY 250 +#endif + + +void rtems_bsdnet_do_dhcp_failsafe (void); + +/** Set the DHCP fallback options. See the commentary at the top of the + * implementation. + @note Some of these options can be compile-time disabled - see the code. + */ +void rtems_bsdnet_dhcp_failsafe_config( + int network_fail_timeout, /**< The number of seconds before the interface is + * considered disconnected + */ + int network_down_time, /**< The number of seconds the interface + * remains down. + */ + int broadcast_delay, /**< The delay in seconds before broadcasts + * are sent. + */ + int dhcp_monitor_priority /**< The monitor priority.*/ +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/rtems/rtems_glue.c b/rtems/rtems_glue.c new file mode 100644 index 0000000..ee6c8f5 --- /dev/null +++ b/rtems/rtems_glue.c @@ -0,0 +1,1264 @@ +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define RTEMS_FAST_MUTEX + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "loop.h" + +/* + * Memory allocation + */ +static uint32_t nmbuf = (64L * 1024L) / _SYS_MBUF_LEGACY_MSIZE; + uint32_t nmbclusters = (128L * 1024L) / MCLBYTES; + +/* + * Network task synchronization + */ +static rtems_recursive_mutex networkMutex = + RTEMS_RECURSIVE_MUTEX_INITIALIZER("_Network"); +static rtems_id networkDaemonTid; +static uint32_t networkDaemonPriority; +#ifdef RTEMS_SMP +static const cpu_set_t *networkDaemonCpuset = 0; +static size_t networkDaemonCpusetSize = 0; +#endif +static void networkDaemon (void *task_argument); + +/* + * Network timing + */ +int rtems_bsdnet_ticks_per_second; +int rtems_bsdnet_microseconds_per_tick; + +/* + * Callout processing + */ +static rtems_interval ticksWhenCalloutsLastChecked; +struct callout *callfree = NULL; +struct callout calltodo; + +/* + * FreeBSD variables + */ +int nfs_diskless_valid; + +/* + * BOOTP values + */ +struct in_addr rtems_bsdnet_log_host_address = {0}; +struct in_addr rtems_bsdnet_bootp_server_address = {0}; +char *rtems_bsdnet_bootp_boot_file_name = 0; +char *rtems_bsdnet_bootp_server_name = 0; +char *rtems_bsdnet_domain_name = 0; +char *rtems_bsdnet_bootp_cmdline = 0; +static struct in_addr _rtems_bsdnet_nameserver[sizeof rtems_bsdnet_config.name_server / + sizeof rtems_bsdnet_config.name_server[0]]; +struct in_addr *rtems_bsdnet_nameserver = _rtems_bsdnet_nameserver; +int rtems_bsdnet_nameserver_count = 0; +static struct in_addr _rtems_bsdnet_ntpserver[sizeof rtems_bsdnet_config.ntp_server / + sizeof rtems_bsdnet_config.ntp_server[0]]; +struct in_addr *rtems_bsdnet_ntpserver = _rtems_bsdnet_ntpserver; +int rtems_bsdnet_ntpserver_count = 0; +int32_t rtems_bsdnet_timeoffset = 0; + +static const struct sockaddr_in address_template = { + sizeof(address_template), + AF_INET, + 0, + { INADDR_ANY }, + { 0, 0, 0, 0, 0, 0, 0, 0 } +}; + +static void +rtems_bsdnet_initialize_sockaddr_in(struct sockaddr_in *addr) +{ + memcpy(addr, &address_template, sizeof(*addr)); +} + +uint32_t +rtems_bsdnet_semaphore_release_recursive(void) +{ + uint32_t nest_count; + + nest_count = networkMutex._nest_level; + networkMutex._nest_level = 0; + rtems_recursive_mutex_unlock(&networkMutex); + return nest_count; +} + +void +rtems_bsdnet_semaphore_obtain_recursive(uint32_t nest_count) +{ + rtems_recursive_mutex_lock(&networkMutex); + networkMutex._nest_level = nest_count; +} + +/* + * Perform FreeBSD memory allocation. + * FIXME: This should be modified to keep memory allocation statistics. + */ +#undef malloc +#undef free +extern void *malloc (size_t); +extern void free (void *); +void * +rtems_bsdnet_malloc (size_t size, int type, int flags) +{ + void *p; + int try = 0; + + for (;;) { + uint32_t nest_count; + + p = malloc (size); + if (p || (flags & M_NOWAIT)) + return p; + nest_count = rtems_bsdnet_semaphore_release_recursive (); + if (++try >= 30) { + rtems_bsdnet_malloc_starvation(); + try = 0; + } + rtems_task_wake_after (rtems_bsdnet_ticks_per_second); + rtems_bsdnet_semaphore_obtain_recursive (nest_count); + } +} + +/* + * Free FreeBSD memory + * FIXME: This should be modified to keep memory allocation statistics. + */ +void +rtems_bsdnet_free (void *addr, int type) +{ + free (addr); +} + +/* + * Externs for BSD data we have to access during initialization + */ +extern struct domain routedomain; +extern struct domain inetdomain; + +/* + * Do the initializations required by the BSD code + */ +static int +bsd_init (void) +{ + int i; + char *p; + + /* + * Set up mbuf cluster data strutures + */ + p = rtems_bsdnet_malloc_mbuf ((nmbclusters*MCLBYTES)+MCLBYTES-1, MBUF_MALLOC_NMBCLUSTERS); + if (p == NULL) { + printf ("Can't get network cluster memory.\n"); + return -1; + } + p = (char *)(((intptr_t)p + (MCLBYTES-1)) & ~(MCLBYTES-1)); + mbutl = (struct mbuf *)p; + for (i = 0; i < nmbclusters; i++) { + ((union mcluster *)p)->mcl_next = mclfree; + mclfree = (union mcluster *)p; + p += MCLBYTES; + mbstat.m_clfree++; + } + mbstat.m_clusters = nmbclusters; + mclrefcnt = rtems_bsdnet_malloc_mbuf (nmbclusters, MBUF_MALLOC_MCLREFCNT); + if (mclrefcnt == NULL) { + printf ("Can't get mbuf cluster reference counts memory.\n"); + return -1; + } + memset (mclrefcnt, '\0', nmbclusters); + + /* + * Set up mbuf data structures + */ + + p = rtems_bsdnet_malloc_mbuf(nmbuf * _SYS_MBUF_LEGACY_MSIZE + _SYS_MBUF_LEGACY_MSIZE - 1,MBUF_MALLOC_MBUF); + p = (char *)(((uintptr_t)p + _SYS_MBUF_LEGACY_MSIZE - 1) & ~(_SYS_MBUF_LEGACY_MSIZE - 1)); + if (p == NULL) { + printf ("Can't get network memory.\n"); + return -1; + } + for (i = 0; i < nmbuf; i++) { + ((struct mbuf *)p)->m_next = mmbfree; + mmbfree = (struct mbuf *)p; + p += _SYS_MBUF_LEGACY_MSIZE; + } + mbstat.m_mbufs = nmbuf; + mbstat.m_mtypes[MT_FREE] = nmbuf; + + /* + * Set up domains + */ + { + + routedomain.dom_next = domains; + domains = &routedomain; + inetdomain.dom_next = domains; + domains = &inetdomain; + domaininit (NULL); + } + + /* + * Setup the sysctl, normally done by a SYSINIT call. + */ + sysctl_register_all(0); + + /* + * Set up interfaces + */ + ifinit (NULL); + return 0; +} + +/* + * Initialize and start network operations + */ +static int +rtems_bsdnet_initialize (void) +{ + rtems_bsdnet_semaphore_obtain (); + + /* + * Set the priority of all network tasks + */ + if (rtems_bsdnet_config.network_task_priority == 0) + networkDaemonPriority = 100; +#ifdef RTEMS_MULTIPROCESSING + /* + * Allow network tasks to run with priority 0 (PRIORITY_PSEUDO_ISR) using + * UINT32_MAX for the network task priority in the network configuration. + * This enables MPCI via a TCP/IP network. + */ + else if (rtems_bsdnet_config.network_task_priority != UINT32_MAX) +#else + else +#endif + networkDaemonPriority = rtems_bsdnet_config.network_task_priority; + + /* + * Default network task CPU affinity + */ +#ifdef RTEMS_SMP + networkDaemonCpuset = rtems_bsdnet_config.network_task_cpuset; + networkDaemonCpusetSize = rtems_bsdnet_config.network_task_cpuset_size; +#endif + + /* + * Set the memory allocation limits + */ + if (rtems_bsdnet_config.mbuf_bytecount) + nmbuf = rtems_bsdnet_config.mbuf_bytecount / _SYS_MBUF_LEGACY_MSIZE; + if (rtems_bsdnet_config.mbuf_cluster_bytecount) + nmbclusters = rtems_bsdnet_config.mbuf_cluster_bytecount / MCLBYTES; + + rtems_set_udp_buffer_sizes( + rtems_bsdnet_config.udp_tx_buf_size, + rtems_bsdnet_config.udp_rx_buf_size + ); + + rtems_set_tcp_buffer_sizes( + rtems_bsdnet_config.tcp_tx_buf_size, + rtems_bsdnet_config.tcp_rx_buf_size + ); + + rtems_set_sb_efficiency( rtems_bsdnet_config.sb_efficiency ); + + /* + * Compute clock tick conversion factors + */ + rtems_bsdnet_ticks_per_second = rtems_clock_get_ticks_per_second(); + if (rtems_bsdnet_ticks_per_second <= 0) + rtems_bsdnet_ticks_per_second = 1; + rtems_bsdnet_microseconds_per_tick = + 1000000 / rtems_bsdnet_ticks_per_second; + + /* + * Set up BSD-style sockets + */ + if (bsd_init () < 0) { + rtems_bsdnet_semaphore_release (); + return -1; + } + + /* + * Start network daemon + */ + networkDaemonTid = rtems_bsdnet_newproc ("ntwk", 4096, networkDaemon, NULL); + + /* + * Let other network tasks begin + */ + rtems_bsdnet_semaphore_release (); + + rtems_bsdnet_initialize_loop(); + + return 0; +} + +/* + * Obtain network mutex + */ +void +rtems_bsdnet_semaphore_obtain (void) +{ + rtems_recursive_mutex_lock(&networkMutex); +} + +/* + * Release network mutex + */ +void +rtems_bsdnet_semaphore_release (void) +{ + rtems_recursive_mutex_unlock(&networkMutex); +} + +static int +rtems_bsdnet_sleep(rtems_event_set in, rtems_interval ticks) +{ + rtems_status_code sc; + rtems_event_set out; + rtems_event_set out2; + + in |= RTEMS_EVENT_SYSTEM_NETWORK_CLOSE; + + /* + * Soak up any pending events. The sleep/wakeup synchronization in the + * FreeBSD kernel has no memory. + */ + rtems_event_system_receive(in, RTEMS_EVENT_ANY | RTEMS_NO_WAIT, + RTEMS_NO_TIMEOUT, &out); + + /* + * Wait for the wakeup event. + */ + sc = rtems_bsdnet_event_receive(in, RTEMS_EVENT_ANY | RTEMS_WAIT, + ticks, &out); + + /* + * Get additional events that may have been received between the + * rtems_event_system_receive() and the rtems_bsdnet_semaphore_obtain(). + */ + rtems_event_system_receive(in, RTEMS_EVENT_ANY | RTEMS_NO_WAIT, + RTEMS_NO_TIMEOUT, &out2); + out |= out2; + + if (out & RTEMS_EVENT_SYSTEM_NETWORK_CLOSE) + return (ENXIO); + + if (sc == RTEMS_SUCCESSFUL) + return (0); + + return (EWOULDBLOCK); +} + +/* + * Wait for something to happen to a socket buffer + */ +int +sbwait(struct sockbuf *sb) +{ + int error; + + /* + * Set this task as the target of the wakeup operation. + */ + sb->sb_sel.si_pid = rtems_task_self(); + + /* + * Show that socket is waiting + */ + sb->sb_flags |= SB_WAIT; + + error = rtems_bsdnet_sleep(SBWAIT_EVENT, sb->sb_timeo); + if (error != ENXIO) + sb->sb_flags &= ~SB_WAIT; + + return (error); +} + + +/* + * Wake up the task waiting on a socket buffer. + */ +void +sowakeup( + struct socket *so, + struct sockbuf *sb) +{ + if (sb->sb_flags & SB_WAIT) { + rtems_event_system_send (sb->sb_sel.si_pid, SBWAIT_EVENT); + } + if (sb->sb_wakeup) { + (*sb->sb_wakeup) (so, sb->sb_wakeuparg); + } +} + +/* + * For now, a socket can be used by only one task at a time. + */ +int +sb_lock(struct sockbuf *sb) +{ + rtems_panic ("Socket buffer is already in use."); + return 0; +} +void +wakeup (void *p) +{ + rtems_panic ("Wakeup called"); +} + +/* + * Wait for a connection/disconnection event. + */ +int +soconnsleep (struct socket *so) +{ + int error; + + /* + * Set this task as the target of the wakeup operation. + */ + if (so->so_pgid) + rtems_panic ("Another task is already sleeping on that socket"); + so->so_pgid = rtems_task_self(); + + error = rtems_bsdnet_sleep(SOSLEEP_EVENT, so->so_rcv.sb_timeo); + if (error != ENXIO) + so->so_pgid = 0; + + return (error); +} + +/* + * Wake up a task waiting for a connection/disconnection to complete. + */ +void +soconnwakeup (struct socket *so) +{ + if (so->so_pgid) + rtems_event_system_send (so->so_pgid, SOSLEEP_EVENT); +} + +/* + * Send an event to the network daemon. + * This corresponds to sending a software interrupt in the BSD kernel. + */ +void +rtems_bsdnet_schednetisr (int n) +{ + rtems_event_system_send (networkDaemonTid, 1 << n); +} + +/* + * The network daemon + * This provides a context to run BSD software interrupts + */ +static void +networkDaemon (void *task_argument) +{ + rtems_status_code sc; + rtems_event_set events; + rtems_interval now; + int ticksPassed; + uint32_t timeout; + struct callout *c; + + for (;;) { + c = calltodo.c_next; + if (c) + timeout = c->c_time; + else + timeout = RTEMS_NO_TIMEOUT; + + sc = rtems_bsdnet_event_receive (NETISR_EVENTS, + RTEMS_EVENT_ANY | RTEMS_WAIT, + timeout, + &events); + if ( sc == RTEMS_SUCCESSFUL ) { + if (events & NETISR_IP_EVENT) + ipintr (); + if (events & NETISR_ARP_EVENT) + arpintr (); + } + + now = rtems_clock_get_ticks_since_boot(); + ticksPassed = now - ticksWhenCalloutsLastChecked; + if (ticksPassed != 0) { + ticksWhenCalloutsLastChecked = now; + + c = calltodo.c_next; + if (c) { + c->c_time -= ticksPassed; + while ((c = calltodo.c_next) != NULL && c->c_time <= 0) { + void *arg; + void (*func) (void *); + + func = c->c_func; + arg = c->c_arg; + calltodo.c_next = c->c_next; + c->c_next = callfree; + callfree = c; + (*func)(arg); + } + } + } + } +} + +/* + * Structure passed to task-start stub + */ +struct newtask { + void (*entry)(void *); + void *arg; +}; + +/* + * Task-start stub + */ +static void +taskEntry (rtems_task_argument arg) +{ + struct newtask t; + + /* + * Pick up task information and free + * the memory allocated to pass the + * information to this task. + */ + t = *(struct newtask *)arg; + free ((struct newtask *)arg); + + /* + * Enter the competition for the network semaphore + */ + rtems_bsdnet_semaphore_obtain (); + + /* + * Enter the task + */ + (*t.entry)(t.arg); + rtems_panic ("Network task returned!\n"); +} + + +/* + * Start a network task + */ +#ifdef RTEMS_SMP +rtems_id +rtems_bsdnet_newproc (char *name, int stacksize, void(*entry)(void *), void *arg) +{ + return rtems_bsdnet_newproc_affinity( name, stacksize, entry, arg, + networkDaemonCpuset, networkDaemonCpusetSize ); +} + +rtems_id +rtems_bsdnet_newproc_affinity (char *name, int stacksize, void(*entry)(void *), + void *arg, const cpu_set_t *set, const size_t setsize) +#else +rtems_id +rtems_bsdnet_newproc (char *name, int stacksize, void(*entry)(void *), void *arg) +#endif +{ + struct newtask *t; + char nm[4]; + rtems_id tid; + rtems_status_code sc; + + strncpy (nm, name, 4); + sc = rtems_task_create (rtems_build_name(nm[0], nm[1], nm[2], nm[3]), + networkDaemonPriority, + stacksize, + RTEMS_PREEMPT|RTEMS_NO_TIMESLICE|RTEMS_NO_ASR|RTEMS_INTERRUPT_LEVEL(0), +#ifdef RTEMS_MULTIPROCESSING + RTEMS_SYSTEM_TASK | +#endif + RTEMS_NO_FLOATING_POINT|RTEMS_LOCAL, + &tid); + if (sc != RTEMS_SUCCESSFUL) + rtems_panic ("Can't create network daemon `%s': `%s'\n", name, rtems_status_text (sc)); + +#ifdef RTEMS_SMP + /* + * Use the default affinity or use the user-provided CPU set + */ + if ( set != 0 ) + rtems_task_set_affinity( tid, setsize, set ); +#endif + + /* + * Set up task arguments + */ + t = malloc (sizeof *t); + t->entry = entry; + t->arg = arg; + + /* + * Start the task + */ + sc = rtems_task_start (tid, taskEntry, (rtems_task_argument)t); + if (sc != RTEMS_SUCCESSFUL) + rtems_panic ("Can't start network daemon `%s': `%s'\n", name, rtems_status_text (sc)); + + /* + * Let our caller know the i.d. of the new task + */ + return tid; +} + +rtems_status_code rtems_bsdnet_event_receive ( + rtems_event_set event_in, + rtems_option option_set, + rtems_interval ticks, + rtems_event_set *event_out) +{ + rtems_status_code sc; + + rtems_bsdnet_semaphore_release (); + sc = rtems_event_system_receive (event_in, option_set, ticks, event_out); + rtems_bsdnet_semaphore_obtain (); + return sc; +} + +/* + * Fake random number generator + */ +unsigned long +rtems_bsdnet_random (void) +{ + rtems_interval now; + + now = rtems_clock_get_ticks_since_boot(); + return (now * 99991); +} + +/* + * Callout list processing + */ +void +rtems_bsdnet_timeout(void (*ftn)(void *), void *arg, int ticks) +{ + register struct callout *new, *p, *t; + + if (ticks <= 0) + ticks = 1; + + /* Fill in the next free callout structure. */ + if (callfree == NULL) { + callfree = malloc (sizeof *callfree); + if (callfree == NULL) + rtems_panic ("No memory for timeout table entry"); + callfree->c_next = NULL; + } + + new = callfree; + callfree = new->c_next; + new->c_arg = arg; + new->c_func = ftn; + + /* + * The time for each event is stored as a difference from the time + * of the previous event on the queue. Walk the queue, correcting + * the ticks argument for queue entries passed. Correct the ticks + * value for the queue entry immediately after the insertion point + * as well. Watch out for negative c_time values; these represent + * overdue events. + */ + for (p = &calltodo; + (t = p->c_next) != NULL && ticks > t->c_time; p = t) + if (t->c_time > 0) + ticks -= t->c_time; + new->c_time = ticks; + if (t != NULL) + t->c_time -= ticks; + + /* Insert the new entry into the queue. */ + p->c_next = new; + new->c_next = t; +} + +/* + * Ticks till specified time + * XXX: This version worries only about seconds, but that's good + * enough for the way the network code uses this routine. + */ +int +hzto(struct timeval *tv) +{ + long diff = tv->tv_sec - rtems_bsdnet_seconds_since_boot(); + + if (diff <= 0) + return 1; + return diff * rtems_bsdnet_ticks_per_second; +} + +/* + * Kernel debugging + */ +int rtems_bsdnet_log_priority; +void +rtems_bsdnet_log (int priority, const char *fmt, ...) +{ + va_list args; + + if (priority & rtems_bsdnet_log_priority) { + va_start (args, fmt); + vprintf (fmt, args); + va_end (args); + } +} + +/* + * IP header checksum routine for processors which don't have an inline version + */ + +struct ip; + +u_int in_cksum_hdr(const struct ip *); + +u_int +in_cksum_hdr (const struct ip *ip) +{ + uint32_t sum; + const uint16_t *sp; + int i; + + sum = 0; + sp = (uint16_t *)ip; + for (i = 0 ; i < 10 ; i++) + sum += *sp++; + while (sum > 0xFFFF) + sum = (sum & 0xffff) + (sum >> 16); + return ~sum & 0xFFFF; +} + +/* + * Manipulate routing tables + */ +int rtems_bsdnet_rtrequest ( + int req, + struct sockaddr *dst, + struct sockaddr *gateway, + struct sockaddr *netmask, + int flags, + struct rtentry **net_nrt) +{ + int error; + + rtems_bsdnet_semaphore_obtain (); + error = rtrequest (req, dst, gateway, netmask, flags, net_nrt); + rtems_bsdnet_semaphore_release (); + if (error) { + errno = error; + return -1; + } + return 0; +} + +static bool +rtems_bsdnet_setup_interface( + const char *name, + const char *ip_address, + const char *ip_netmask +) +{ + struct sockaddr_in address; + struct sockaddr_in netmask; + short flags; + + /* + * Bring interface up + */ + flags = IFF_UP; + if (rtems_bsdnet_ifconfig (name, SIOCSIFFLAGS, &flags) < 0) { + printf ("Can't bring %s up: %s\n", name, strerror (errno)); + return false; + } + + /* + * Set interface netmask + */ + rtems_bsdnet_initialize_sockaddr_in(&netmask); + netmask.sin_addr.s_addr = inet_addr (ip_netmask); + if (rtems_bsdnet_ifconfig (name, SIOCSIFNETMASK, &netmask) < 0) { + printf ("Can't set %s netmask: %s\n", name, strerror (errno)); + return false; + } + + /* + * Set interface address + */ + rtems_bsdnet_initialize_sockaddr_in(&address); + address.sin_addr.s_addr = inet_addr (ip_address); + if (rtems_bsdnet_ifconfig (name, SIOCSIFADDR, &address) < 0) { + printf ("Can't set %s address: %s\n", name, strerror (errno)); + return false; + } + + /* + * Set interface broadcast address if the interface has the + * broadcast flag set. + */ + if (rtems_bsdnet_ifconfig (name, SIOCGIFFLAGS, &flags) < 0) { + printf ("Can't read %s flags: %s\n", name, strerror (errno)); + return false; + } + + if (flags & IFF_BROADCAST) { + struct sockaddr_in broadcast; + + rtems_bsdnet_initialize_sockaddr_in(&broadcast); + broadcast.sin_addr.s_addr = + address.sin_addr.s_addr | ~netmask.sin_addr.s_addr; + if (rtems_bsdnet_ifconfig (name, SIOCSIFBRDADDR, &broadcast) < 0) { + struct in_addr in_addr; + char buf[20]; + in_addr.s_addr = broadcast.sin_addr.s_addr; + if (!inet_ntop(AF_INET, &in_addr, buf, sizeof(buf))) + strcpy(buf,"?.?.?.?"); + printf ("Can't set %s broadcast address %s: %s\n", + name, buf, strerror (errno)); + } + } + + return true; +} + +static int +rtems_bsdnet_setup (void) +{ + struct rtems_bsdnet_ifconfig *ifp; + int i; + bool any_if_configured = false; + + /* + * Set local parameters + */ + if (rtems_bsdnet_config.hostname) + sethostname (rtems_bsdnet_config.hostname, + strlen (rtems_bsdnet_config.hostname)); + if (rtems_bsdnet_config.domainname) + rtems_bsdnet_domain_name = + strdup (rtems_bsdnet_config.domainname); + if (rtems_bsdnet_config.log_host) + rtems_bsdnet_log_host_address.s_addr = + inet_addr (rtems_bsdnet_config.log_host); + for (i = 0 ; i < sizeof rtems_bsdnet_config.name_server / + sizeof rtems_bsdnet_config.name_server[0] ; i++) { + if (!rtems_bsdnet_config.name_server[i]) + break; + rtems_bsdnet_nameserver[rtems_bsdnet_nameserver_count++].s_addr + = inet_addr (rtems_bsdnet_config.name_server[i]); + } + for (i = 0 ; i < sizeof rtems_bsdnet_config.ntp_server / + sizeof rtems_bsdnet_config.ntp_server[0] ; i++) { + if (!rtems_bsdnet_config.ntp_server[i]) + break; + rtems_bsdnet_ntpserver[rtems_bsdnet_ntpserver_count++].s_addr + = inet_addr (rtems_bsdnet_config.ntp_server[i]); + } + + /* + * Configure interfaces + */ + any_if_configured |= rtems_bsdnet_setup_interface( + "lo0", + "127.0.0.1", + "255.0.0.0" + ); + for (ifp = rtems_bsdnet_config.ifconfig ; ifp ; ifp = ifp->next) { + if (ifp->ip_address == NULL) + continue; + + any_if_configured |= rtems_bsdnet_setup_interface( + ifp->name, + ifp->ip_address, + ifp->ip_netmask + ); + } + + /* + * Set default route + */ + if (rtems_bsdnet_config.gateway && any_if_configured) { + struct sockaddr_in address; + struct sockaddr_in netmask; + struct sockaddr_in gateway; + + rtems_bsdnet_initialize_sockaddr_in(&address); + rtems_bsdnet_initialize_sockaddr_in(&netmask); + rtems_bsdnet_initialize_sockaddr_in(&gateway); + + gateway.sin_addr.s_addr = inet_addr (rtems_bsdnet_config.gateway); + + if (rtems_bsdnet_rtrequest ( + RTM_ADD, + (struct sockaddr *)&address, + (struct sockaddr *)&gateway, + (struct sockaddr *)&netmask, + (RTF_UP | RTF_GATEWAY | RTF_STATIC), NULL) < 0) { + printf ("Can't set default route: %s\n", strerror (errno)); + return -1; + } + } + return 0; +} + +/* + * Initialize the network + */ +int +rtems_bsdnet_initialize_network(void) +{ + struct rtems_bsdnet_ifconfig *ifp; + + /* + * Start network tasks. + * Initialize BSD network data structures. + */ + if (rtems_bsdnet_initialize () < 0) + return -1; + + /* + * Attach interfaces + */ + for (ifp = rtems_bsdnet_config.ifconfig ; ifp ; ifp = ifp->next) { + rtems_bsdnet_attach (ifp); + } + + /* + * Bring up the network + */ + if (rtems_bsdnet_setup () < 0) + return -1; + if (rtems_bsdnet_config.bootp) + (*rtems_bsdnet_config.bootp)(); + return 0; +} + +/* + * Attach a network interface. + */ +void rtems_bsdnet_attach(struct rtems_bsdnet_ifconfig *ifp) +{ + if (ifp) { + rtems_bsdnet_semaphore_obtain (); + (ifp->attach)(ifp, 1); + rtems_bsdnet_semaphore_release (); + } +} + +/* + * Detach a network interface. + */ +void rtems_bsdnet_detach (struct rtems_bsdnet_ifconfig *ifp) +{ + if (ifp) { + rtems_bsdnet_semaphore_obtain (); + (ifp->attach)(ifp, 0); + rtems_bsdnet_semaphore_release (); + } +} + +/* + * Interface Configuration. + */ +int rtems_bsdnet_ifconfig(const char *ifname, uint32_t cmd, void *param) +{ + int s, r = 0; + struct ifreq ifreq; + + /* + * Configure interfaces + */ + s = socket (AF_INET, SOCK_DGRAM, 0); + if (s < 0) + return -1; + + strncpy (ifreq.ifr_name, ifname, IFNAMSIZ); + + rtems_bsdnet_semaphore_obtain (); + + switch (cmd) { + case SIOCSIFADDR: + case SIOCSIFNETMASK: + memcpy (&ifreq.ifr_addr, param, sizeof (struct sockaddr)); + r = ioctl (s, cmd, &ifreq); + break; + + case OSIOCGIFADDR: + case SIOCGIFADDR: + case OSIOCGIFNETMASK: + case SIOCGIFNETMASK: + if ((r = ioctl (s, cmd, &ifreq)) < 0) + break; + memcpy (param, &ifreq.ifr_addr, sizeof (struct sockaddr)); + break; + + case SIOCGIFFLAGS: + case SIOCSIFFLAGS: + if ((r = ioctl (s, SIOCGIFFLAGS, &ifreq)) < 0) + break; + if (cmd == SIOCGIFFLAGS) { + *((short*) param) = ifreq.ifr_flags; + break; + } + ifreq.ifr_flags |= *((short*) param); + if ( (*((short*) param) & IFF_UP ) == 0 ) { + /* set the interface down */ + ifreq.ifr_flags &= ~(IFF_UP); + } + r = ioctl (s, SIOCSIFFLAGS, &ifreq); + break; + + case SIOCSIFDSTADDR: + memcpy (&ifreq.ifr_dstaddr, param, sizeof (struct sockaddr)); + r = ioctl (s, cmd, &ifreq); + break; + + case OSIOCGIFDSTADDR: + case SIOCGIFDSTADDR: + if ((r = ioctl (s, cmd, &ifreq)) < 0) + break; + memcpy (param, &ifreq.ifr_dstaddr, sizeof (struct sockaddr)); + break; + + case SIOCSIFBRDADDR: + memcpy (&ifreq.ifr_broadaddr, param, sizeof (struct sockaddr)); + r = ioctl (s, cmd, &ifreq); + break; + + case OSIOCGIFBRDADDR: + case SIOCGIFBRDADDR: + if ((r = ioctl (s, cmd, &ifreq)) < 0) + break; + memcpy (param, &ifreq.ifr_broadaddr, sizeof (struct sockaddr)); + break; + + case SIOCSIFMETRIC: + ifreq.ifr_metric = *((int*) param); + r = ioctl (s, cmd, &ifreq); + break; + + case SIOCGIFMETRIC: + if ((r = ioctl (s, cmd, &ifreq)) < 0) + break; + *((int*) param) = ifreq.ifr_metric; + break; + + case SIOCSIFMTU: + ifreq.ifr_mtu = *((int*) param); + r = ioctl (s, cmd, &ifreq); + break; + + case SIOCGIFMTU: + if ((r = ioctl (s, cmd, &ifreq)) < 0) + break; + *((int*) param) = ifreq.ifr_mtu; + break; + + case SIOCSIFPHYS: + ifreq.ifr_phys = *((int*) param); + r = ioctl (s, cmd, &ifreq); + break; + + case SIOCGIFPHYS: + if ((r = ioctl (s, cmd, &ifreq)) < 0) + break; + *((int*) param) = ifreq.ifr_phys; + break; + + case SIOCSIFMEDIA: + ifreq.ifr_media = *((int*) param); + r = ioctl (s, cmd, &ifreq); + break; + + case SIOCGIFMEDIA: + /* 'param' passes the phy index they want to + * look at... + */ + ifreq.ifr_media = *((int*) param); + if ((r = ioctl (s, cmd, &ifreq)) < 0) + break; + *((int*) param) = ifreq.ifr_media; + break; + + case SIOCAIFADDR: + case SIOCDIFADDR: + r = ioctl(s, cmd, (struct ifreq *) param); + break; + + default: + errno = EOPNOTSUPP; + r = -1; + break; + } + + rtems_bsdnet_semaphore_release (); + + close (s); + return r; +} + +/** + * @brief Splits a network interface name with interface configuration @a + * config into the unit name and number parts. + * + * Memory for the unit name will be allocated from the heap and copied to @a + * namep. If @a namep is NULL nothing will be allocated and copied. + * + * Returns the unit number or -1 on error. + */ +int +rtems_bsdnet_parse_driver_name (const struct rtems_bsdnet_ifconfig *config, char **namep) +{ + const char *cp = config->name; + char c; + int unitNumber = 0; + + if (cp == NULL) { + printf ("No network driver name.\n"); + return -1; + } + while ((c = *cp++) != '\0') { + if ((c >= '0') && (c <= '9')) { + int len = cp - config->name; + if ((len < 2) || (len > 50)) + break; + for (;;) { + unitNumber = (unitNumber * 10) + (c - '0'); + c = *cp++; + if (c == '\0') { + if (namep != NULL) { + char *unitName = malloc (len); + if (unitName == NULL) { + printf ("No memory.\n"); + return -1; + } + strncpy (unitName, config->name, len - 1); + unitName[len-1] = '\0'; + *namep = unitName; + } + return unitNumber; + } + if ((c < '0') || (c > '9')) + break; + } + break; + } + } + printf ("Bad network driver name `%s'.\n", config->name); + return -1; +} + +/* + * Handle requests for more network memory + * XXX: Another possibility would be to use a semaphore here with + * a release in the mbuf free macro. I have chosen this `polling' + * approach because: + * 1) It is simpler. + * 2) It adds no complexity to the free macro. + * 3) Running out of mbufs should be a rare + * condition -- predeployment testing of + * an application should indicate the + * required mbuf pool size. + * XXX: Should there be a panic if a task is stuck in the loop for + * more than a minute or so? + */ +int +m_mballoc(int nmb, int nowait) +{ + if (nowait) + return 0; + m_reclaim (); + if (mmbfree == NULL) { + int try = 0; + int print_limit = 30 * rtems_bsdnet_ticks_per_second; + + mbstat.m_wait++; + for (;;) { + uint32_t nest_count = rtems_bsdnet_semaphore_release_recursive (); + rtems_task_wake_after (1); + rtems_bsdnet_semaphore_obtain_recursive (nest_count); + if (mmbfree) + break; + if (++try >= print_limit) { + printf ("Still waiting for mbuf.\n"); + try = 0; + } + } + } + else { + mbstat.m_drops++; + } + return 1; +} + +int +m_clalloc(int ncl, int nowait) +{ + if (nowait) + return 0; + m_reclaim (); + if (mclfree == NULL) { + int try = 0; + int print_limit = 30 * rtems_bsdnet_ticks_per_second; + + mbstat.m_wait++; + for (;;) { + uint32_t nest_count = rtems_bsdnet_semaphore_release_recursive (); + rtems_task_wake_after (1); + rtems_bsdnet_semaphore_obtain_recursive (nest_count); + if (mclfree) + break; + if (++try >= print_limit) { + printf ("Still waiting for mbuf cluster.\n"); + try = 0; + } + } + } + else { + mbstat.m_drops++; + } + return 1; +} + diff --git a/rtems/rtems_malloc_mbuf.c b/rtems/rtems_malloc_mbuf.c new file mode 100644 index 0000000..a12e329 --- /dev/null +++ b/rtems/rtems_malloc_mbuf.c @@ -0,0 +1,33 @@ +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define RTEMS_FAST_MUTEX + +#include +#include +#include +#include + +#include +#include + +/* + * We want to use the REAL system malloc. Do not let the BSD malloc macro + * invade this file. + */ +extern void *malloc(size_t); + +/* + * Default allocator for mbuf data. Over-ride in user code to change + * the way mbuf's are allocated. + */ + +void* rtems_bsdnet_malloc_mbuf(size_t size, int type) +{ + return malloc(size); +} + + diff --git a/rtems/rtems_mii_ioctl.c b/rtems/rtems_mii_ioctl.c new file mode 100644 index 0000000..2559f4b --- /dev/null +++ b/rtems/rtems_mii_ioctl.c @@ -0,0 +1,172 @@ +#include + +/* Simple (default) implementation for SIOCGIFMEDIA/SIOCSIFMEDIA + * to be used by ethernet drivers [from their ioctl]. + * + * USERSPACE UTILITIES + * + * NOTE: This much simpler than the BSD ifmedia API + */ + +/* + * Authorship + * ---------- + * This software was created by + * Till Straumann , 2005, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * This software was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +#include +#include + +#undef _KERNEL + +#include + +#include +#include +#include +#include +#include + +static struct ifmedia_description shared_media_strings[] = + IFM_SUBTYPE_SHARED_DESCRIPTIONS; +static struct ifmedia_description ethern_media_strings[] = + IFM_SUBTYPE_ETHERNET_DESCRIPTIONS; +static struct ifmedia_description eth_al_media_strings[] = + IFM_SUBTYPE_ETHERNET_ALIASES; + +static const char * +find_desc (int tag, struct ifmedia_description *list) +{ + while (list->ifmt_string && tag != list->ifmt_word) + list++; + return list->ifmt_string; +} + +#define WHATPRINT(buf,sz,fmt...) \ + ( (sz) > 0 ? snprintf((buf),(sz),fmt) : fprintf((buf) ? (FILE*)(buf) : stdout, fmt) ) + +int +rtems_ifmedia2str (int media, char *buf, int bufsz) +{ + const char *mdesc; + const char *dupdesc = 0; + + /* only ethernet supported, so far */ + if (IFM_ETHER != IFM_TYPE (media)) + return -1; + + if (!(mdesc = find_desc (IFM_SUBTYPE (media), shared_media_strings))) + mdesc = find_desc (IFM_SUBTYPE (media), ethern_media_strings); + + if (!mdesc) + return -1; + + if (IFM_NONE != IFM_SUBTYPE (media)) + dupdesc = IFM_FDX & media ? " full-duplex" : " half-duplex"; + + return WHATPRINT (buf, bufsz, + "Ethernet [phy instance: %" PRId32 "]: (link %s, autoneg %s) -- media: %s%s", + (int32_t) IFM_INST (media), + IFM_LINK_OK & media ? "ok" : "down", + IFM_ANEG_DIS & media ? "off" : "on", + mdesc, dupdesc ? dupdesc : ""); +} + +static int +find_tag (const char *desc, struct ifmedia_description *list) +{ + while (list->ifmt_string) { + if (strstr (desc, list->ifmt_string)) + return list->ifmt_word; + list++; + } + return -1; +} + + +/* convert a string to a media word + * RETURNS: 0 on failure; valid results have always at least IFM_ETHER set + */ +int +rtems_str2ifmedia (const char *str, int phy) +{ + int sub, opt = 0; + char *chpt; + + if (!strncmp (str, "auto", 4)) { + sub = IFM_AUTO; + } else if ((sub = find_tag (str, ethern_media_strings)) < 0) { + if ((sub = find_tag (str, eth_al_media_strings)) < 0) { + /* allow more */ + + /* if no number, 0 is returned which will not pass the test */ + switch (strtol (str, &chpt, 10)) { + case 10: + sub = IFM_10_T; + break; + case 100: + sub = IFM_100_TX; + break; + case 1000: + sub = IFM_1000_T; + break; + default: + return 0; + } + + /* need 'b' or 'base' */ + if ('b' != *chpt++) + return 0; + if (!strncmp (chpt, "ase", 3)) + chpt += 3; + if (toupper ((unsigned char)*chpt++) != 'T') + return 0; + if (IFM_100_TX == sub && toupper ((unsigned char)*chpt++) != 'X') + return 0; + } + } + + if (strstr (str, "full") || strstr (str, "FDX") || strstr (str, "fdx")) + opt |= IFM_FDX; + + return IFM_MAKEWORD (IFM_ETHER, sub, opt, phy); +} diff --git a/rtems/rtems_mii_ioctl.h b/rtems/rtems_mii_ioctl.h new file mode 100644 index 0000000..dfeebf1 --- /dev/null +++ b/rtems/rtems_mii_ioctl.h @@ -0,0 +1,139 @@ +/* Simple (default) implementation for SIOCGIFMEDIA/SIOCSIFMEDIA + * to be used by ethernet drivers [from their ioctl]. + * + * NOTE: This much simpler than the BSD ifmedia API + */ + +/* + * Authorship + * ---------- + * This software was created by + * Till Straumann , 2005, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * This software was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ +#ifndef RTEMS_MII_IOCTL_H +#define RTEMS_MII_IOCTL_H + +#include /* MII register definitions */ +#include /* media word definitions; rest of API (ifmedia) unused! */ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_KERNEL) || defined(KERNEL) || \ + defined(__KERNEL) || defined(__KERNEL__) +/* mdio routines to be provided by driver */ + +/* read mii register 'reg' at 'phy' (-1 meaning any/currently active) + * RETURNS 0 on success, -1 otherwise (e.g., illegal phy) + */ +typedef int (*rtems_mdio_read_func) (int phy, void *uarg, unsigned reg, + uint32_t * pval); + +/* write mii register 'reg' at 'phy' (-1 meaning any/currently active) + * RETURNS 0 on success, -1 otherwise (e.g., illegal phy) + */ +typedef int (*rtems_mdio_write_func) (int phy, void *uarg, unsigned reg, + uint32_t val); + +/* Values to this must be provided by the driver */ +struct rtems_mdio_info { + rtems_mdio_read_func mdio_r; + rtems_mdio_write_func mdio_w; + unsigned has_gmii:1; /* supports gigabit */ +}; + +/* Implement SIOCSIFMEDIA/SIOCGIFMEDIA; get/set the current media word. Note + * that this does NOT implement the full BSD 'ifmedia' API; also, it only + * implements IFM_ETHER... + * + * INPUT: + * SIOCGIFMEDIA: the media word must set the phy instance (-1 for 'any') + * + */ +int +rtems_mii_ioctl (struct rtems_mdio_info *info, void *uarg, uint32_t cmd, + int *media); + +#endif + +/* The driver flags have the following meaning (SIOCGIFMEDIA only): + */ +#define IFM_LINK_OK IFM_FLAG0 +#define IFM_ANEG_DIS IFM_FLAG1 /* autoneg. disabled; media forced */ + +/* convert a media word to a string; + * + * RETURNS: number of characters written to 'buf' + * + * INPUT: if 'bufsz' is set to IFMEDIA2STR_PRINT_TO_FILE, 'buf' can be a FILE + * pointer where the info is printed insted. This can be NULL in which + * case 'stdout' is used. + */ + +#define IFMEDIA2STR_PRINT_TO_FILE 0 + +int rtems_ifmedia2str (int media, char *buf, int bufsz); + +/* convert a string to a media word + * RETURNS: 0 on failure (unrecognized or invalid mode); + * valid results have always at least IFM_ETHER set. + * + * In addition to IFM_SUBTYPE_ETHERNET_DESCRIPTIONS and + * IFM_SUBTYPE_ETHERNET_ALIASES, the strings + * + * '10' [ '0' [ '0' ]] 'b' [ 'ase' ] ( 't' | 'T' ) + * (* if 100bT [ 'x' | 'X' ] is required here *) + * + * are recognized (e.g., 10bT, 100bTX) + * + * if any of the strings 'full' or 'FDX' or 'fdx' is present, a full-duplex mode + * is selected (half-duplex otherwise). + * e.g., '100bTx-full' + */ + +int rtems_str2ifmedia (const char *str, int phy); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/rtems/rtems_mii_ioctl_kern.c b/rtems/rtems_mii_ioctl_kern.c new file mode 100644 index 0000000..2944f0a --- /dev/null +++ b/rtems/rtems_mii_ioctl_kern.c @@ -0,0 +1,260 @@ +#include + +/* Simple (default) implementation for SIOCGIFMEDIA/SIOCSIFMEDIA + * to be used by ethernet drivers [from their ioctl]. + * + * KERNEL PART (support for drivers) + * + * NOTE: This much simpler than the BSD ifmedia API + */ + +/* + * Authorship + * ---------- + * This software was created by + * Till Straumann , 2005, + * Stanford Linear Accelerator Center, Stanford University. + * + * Acknowledgement of sponsorship + * ------------------------------ + * This software was produced by + * the Stanford Linear Accelerator Center, Stanford University, + * under Contract DE-AC03-76SFO0515 with the Department of Energy. + * + * Government disclaimer of liability + * ---------------------------------- + * Neither the United States nor the United States Department of Energy, + * nor any of their employees, makes any warranty, express or implied, or + * assumes any legal liability or responsibility for the accuracy, + * completeness, or usefulness of any data, apparatus, product, or process + * disclosed, or represents that its use would not infringe privately owned + * rights. + * + * Stanford disclaimer of liability + * -------------------------------- + * Stanford University makes no representations or warranties, express or + * implied, nor assumes any liability for the use of this software. + * + * Stanford disclaimer of copyright + * -------------------------------- + * Stanford University, owner of the copyright, hereby disclaims its + * copyright and all other rights in this software. Hence, anyone may + * freely use it for any purpose without restriction. + * + * Maintenance of notices + * ---------------------- + * In the interest of clarity regarding the origin and status of this + * SLAC software, this and all the preceding Stanford University notices + * are to remain affixed to any copy or derivative of this software made + * or distributed by the recipient and are to be affixed to any copy of + * software made or distributed by the recipient that contains a copy or + * derivative of this software. + * + * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 + */ + +/* include first to avoid 'malloc' clash with rtems_bsdnet_malloc() hack */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef __KERNEL__ +#define __KERNEL__ +#endif + +#include + +#include + + +#define DEBUG + + +#ifndef MII_1000TCR +#define MII_1000TCR MII_100T2CR +#endif + +#ifndef MII_1000TSR +#define MII_1000TSR MII_100T2SR +#endif + +int +rtems_mii_ioctl (struct rtems_mdio_info *info, void *uarg, uint32_t cmd, + int *media) +{ + uint32_t bmcr, bmsr, aner, bmcr2 = 0, bmsr2 = 0, anar, lpar; + int phy = IFM_INST (*media); + uint32_t tmp; + int subtype = 0, options = 0; + + switch (cmd) { + default: + return EINVAL; + +#ifdef DEBUG + case 0: +#endif + case SIOCGIFMEDIA: + if (info->mdio_r (phy, uarg, MII_BMCR, &bmcr)) + return EINVAL; + /* read BMSR twice to clear latched link status low */ + if (info->mdio_r (phy, uarg, MII_BMSR, &bmsr)) + return EINVAL; + if (info->mdio_r (phy, uarg, MII_BMSR, &bmsr)) + return EINVAL; + if (info->mdio_r (phy, uarg, MII_ANER, &aner)) + return EINVAL; + if (info->has_gmii) { + if (info->mdio_r (phy, uarg, MII_1000TCR, &bmcr2)) + return EINVAL; + if (info->mdio_r (phy, uarg, MII_1000TSR, &bmsr2)) + return EINVAL; + } + + /* link status */ + if (BMSR_LINK & bmsr) + options |= IFM_LINK_OK; + + /* do we have autonegotiation disabled ? */ + if (!(BMCR_AUTOEN & bmcr)) { + options |= IFM_ANEG_DIS; + + /* duplex is enforced */ + options |= BMCR_FDX & bmcr ? IFM_FDX : IFM_HDX; + + /* determine speed */ + switch (BMCR_SPEED (bmcr)) { + case BMCR_S10: + subtype = IFM_10_T; + break; + case BMCR_S100: + subtype = IFM_100_TX; + break; + case BMCR_S1000: + subtype = IFM_1000_T; + break; + default: + return ENOTSUP; /* ?? */ + } + } else if (!(BMSR_LINK & bmsr) || !(BMSR_ACOMP & bmsr)) { + subtype = IFM_NONE; + } else { + /* everything ok on our side */ + + if ( ! (ANER_LPAN & aner) ) { + /* Link partner doesn't autonegotiate --> our settings are the + * result of 'parallel detect' (in particular: duplex status is HALF + * according to the standard!). + * Let them know that something's fishy... + */ + options |= IFM_ANEG_DIS; + } + + tmp = ((bmcr2 << 2) & bmsr2) & (GTSR_LP_1000THDX | GTSR_LP_1000TFDX); + if (tmp) { + if (GTSR_LP_1000TFDX & tmp) + options |= IFM_FDX; + subtype = IFM_1000_T; + } else { + if (info->mdio_r (phy, uarg, MII_ANAR, &anar)) + return EINVAL; + if (info->mdio_r (phy, uarg, MII_ANLPAR, &lpar)) + return EINVAL; + if (ANLPAR_ACK & lpar) { + /* this is a negotiated link; otherwise we merely detect the partner's ability */ + } + tmp = anar & lpar; + if (ANLPAR_TX_FD & tmp) { + options |= IFM_FDX; + subtype = IFM_100_TX; + } else if (ANLPAR_T4 & tmp) { + subtype = IFM_100_T4; + } else if (ANLPAR_TX & tmp) { + subtype = IFM_100_TX; + } else if (ANLPAR_10_FD & tmp) { + options |= IFM_FDX; + subtype = IFM_10_T; + } else { + subtype = IFM_10_T; + } + } + } + + *media = IFM_MAKEWORD (IFM_ETHER, subtype, options, phy); + + break; + +#ifdef DEBUG + case 1: +#endif + case SIOCSIFMEDIA: + if (IFM_ETHER != IFM_TYPE (*media)) + return EINVAL; + + if (info->mdio_r (phy, uarg, MII_BMSR, &bmsr)) + return EINVAL; + + tmp = (IFM_FDX & *media); + + switch (IFM_SUBTYPE (*media)) { + default: + return ENOTSUP; + + case IFM_AUTO: + bmcr = BMCR_AUTOEN | BMCR_STARTNEG; + tmp = 0; + break; + + case IFM_1000_T: + if (!info->has_gmii) + return ENOTSUP; + + if (info->mdio_r (phy, uarg, MII_EXTSR, &bmsr2)) + return EINVAL; + + if (!(bmsr2 & (tmp ? EXTSR_1000TFDX : EXTSR_1000THDX))) + return EOPNOTSUPP; + + /* NOTE: gige standard demands auto-negotiation for gige links. + * Disabling autoneg did NOT work on the PHYs I tried + * (BCM5421S, intel 82540). + * I've seen drivers that simply change what they advertise + * to the desired gig mode and re-negotiate. + * We could do that here, too, but we don't see the point - + * If autoneg works fine then we can as well use it. + */ + bmcr = BMCR_S1000; + break; + + case IFM_100_TX: + if (!(bmsr & (tmp ? BMSR_100TXFDX : BMSR_100TXHDX))) + return EOPNOTSUPP; + bmcr = BMCR_S100; + break; + + case IFM_10_T: + if (!(bmsr & (tmp ? BMSR_10TFDX : BMSR_10THDX))) + return EOPNOTSUPP; + bmcr = BMCR_S10; + break; + } + + if (tmp) + bmcr |= BMCR_FDX; + + if (info->mdio_w (phy, uarg, MII_BMCR, bmcr)) + return EINVAL; + + /* TODO: should we adapt advertised capabilites ? */ + + break; + } + + return 0; +} diff --git a/rtems/rtems_netdb.h b/rtems/rtems_netdb.h new file mode 100644 index 0000000..ca4d3dd --- /dev/null +++ b/rtems/rtems_netdb.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#ifndef _RTEMS_RTEMS_NETDB_H +#define _RTEMS_RTEMS_NETDB_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* DO NOT USE THESE, THEY ARE SUBJECT TO CHANGE AND ARE NOT PORTABLE!!! */ +void _sethosthtent(int); +void _endhosthtent(void); +void _sethostdnsent(int); +void _endhostdnsent(void); +void _setnethtent(int); +void _endnethtent(void); +void _setnetdnsent(int); +void _endnetdnsent(void); +struct hostent * _gethostbyhtname(const char *, int); +struct hostent * _gethostbydnsname(const char *, int); +struct hostent * _gethostbynisname(const char *, int); +struct hostent * _gethostbyhtaddr (const char *, int, int); +struct hostent * _gethostbydnsaddr(const char *, int, int); +struct hostent * _gethostbynisaddr(const char *, int, int); +struct netent * _getnetbyhtname (const char *); +struct netent * _getnetbydnsname(const char *); +struct netent * _getnetbynisname(const char *); +struct netent * _getnetbyhtaddr (unsigned long, int); +struct netent * _getnetbydnsaddr(unsigned long, int); +struct netent * _getnetbynisaddr(unsigned long, int); +void _map_v4v6_address(const char *, char *); +void _map_v4v6_hostent(struct hostent *, char **, int *len); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _RTEMS_RTEMS_NETDB_H */ diff --git a/rtems/rtems_netinet_in.h b/rtems/rtems_netinet_in.h new file mode 100644 index 0000000..53240e4 --- /dev/null +++ b/rtems/rtems_netinet_in.h @@ -0,0 +1,80 @@ +/*- + * Copyright (c) 1982, 1986, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _RTEMS_RTEMS_NETINET_IN_H +#define _RTEMS_RTEMS_NETINET_IN_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* + * Ports > IPPORT_USERRESERVED are reserved + * for servers, not necessarily privileged. (IP_PORTRANGE_DEFAULT) + */ +#define IPPORT_USERRESERVED 5000 + +/* Deprecated in current FreeBSD */ +#define IPCTL_RTEXPIRE 5 /* cloned route expiration time */ +#define IPCTL_RTMINEXPIRE 6 /* min value for expiration time */ +#define IPCTL_RTMAXCACHE 7 /* trigger level for dynamic expire */ + +int in_cksum(struct mbuf *, int); + +/* Firewall hooks */ +struct ip; +typedef int ip_fw_chk_t(struct ip**, int, struct ifnet*, int, struct mbuf**); +typedef int ip_fw_ctl_t(int, struct mbuf**); +extern ip_fw_chk_t *ip_fw_chk_ptr; +extern ip_fw_ctl_t *ip_fw_ctl_ptr; + +/* IP NAT hooks */ +typedef int ip_nat_t(struct ip**, struct mbuf**, struct ifnet*, int); +typedef int ip_nat_ctl_t(int, struct mbuf**); +extern ip_nat_t *ip_nat_ptr; +extern ip_nat_ctl_t *ip_nat_ctl_ptr; +#define IP_NAT_IN 0x00000001 +#define IP_NAT_OUT 0x00000002 + +/* + * Options for use with [gs]etsockopt at the IP level. + * + * The value is stored in an integer. Use negative numbers to avoid conflicts + * with BSD. + */ +#define IP_NAT (-55) /* set/get NAT opts */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _RTEMS_RTEMS_NETINET_IN_H */ diff --git a/rtems/rtems_select.c b/rtems/rtems_select.c new file mode 100644 index 0000000..0b58b6a --- /dev/null +++ b/rtems/rtems_select.c @@ -0,0 +1,179 @@ +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +/* #include */ +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + ********************************************************************* + * RTEMS implementation of select() system call * + ********************************************************************* + */ + +/* + * This implementation is quite restricted: + * Works on sockets only -- no support for other devices! + * A given socket can be in a read-select or a read/recv* by only + * one task at a time. + * A given socket can be in a write-select or a write/send* by only + * one task at a time. + * + * NOTE - select() is a very expensive system call. It should be avoided + * if at all possible. In many cases, rewriting the application + * to use multiple tasks (one per socket) is a better solution. + */ + +static __inline int imin(int a, int b) { return (a < b ? a : b); } +struct socket *rtems_bsdnet_fdToSocket(int fd); + +static int +socket_select (struct socket *so, int which, rtems_id tid) +{ + switch (which) { + + case FREAD: + if (soreadable(so)) + return (1); + so->so_rcv.sb_flags |= SB_WAIT; + so->so_rcv.sb_sel.si_pid = tid; + break; + + case FWRITE: + if (sowriteable(so)) + return (1); + so->so_snd.sb_flags |= SB_WAIT; + so->so_snd.sb_sel.si_pid = tid; + break; + + case 0: + if (so->so_oobmark || (so->so_state & SS_RCVATMARK)) + return (1); + so->so_rcv.sb_sel.si_pid = tid; + break; + } + return (0); +} + +static int +selscan (rtems_id tid, fd_mask **ibits, fd_mask **obits, int nfd, int *retval) +{ + struct socket *so; + int msk, i, fd; + fd_mask bits, bit; + int n = 0; + static int flag[3] = { FREAD, FWRITE, 0 }; + + for (msk = 0; msk < 3; msk++) { + if (ibits[msk] == NULL) + continue; + for (i = 0; i < nfd; i += NFDBITS) { + bits = ibits[msk][i/NFDBITS]; + for (fd = i, bit = 1 ; bits && (fd < nfd) ; fd++, bit <<= 1) { + if ((bits & bit) == 0) + continue; + bits &= ~bit; + so = rtems_bsdnet_fdToSocket (fd); + if (so == NULL) + return (EBADF); + if (socket_select (so, flag[msk], tid)) { + obits[msk][fd/NFDBITS] |= + (1 << (fd % NFDBITS)); + n++; + } + } + } + } + *retval = n; + return (0); +} + +int +select (int nfds, fd_set *__restrict readfds, fd_set *__restrict writefds, + fd_set *__restrict exceptfds, struct timeval *__restrict tv) +{ + fd_mask *ibits[3], *obits[3]; + fd_set ob[3]; + int error, timo; + int retval = 0; + rtems_id tid; + rtems_interval then = 0, now; + rtems_event_set in = SBWAIT_EVENT | RTEMS_EVENT_SYSTEM_NETWORK_CLOSE; + rtems_event_set out; + + if (nfds < 0) + return (EINVAL); + if (tv) { + timo = tv->tv_sec * hz + tv->tv_usec / tick; + if (timo == 0) + timo = 1; + then = rtems_clock_get_ticks_since_boot(); + } + else { + timo = 0; + } + +#define getbits(name,i) if (name) { \ + ibits[i] = &name->fds_bits[0]; \ + obits[i] = &ob[i].fds_bits[0]; \ + FD_ZERO(&ob[i]); \ + } \ + else ibits[i] = NULL + getbits (readfds, 0); + getbits (writefds, 1); + getbits (exceptfds, 2); +#undef getbits + + rtems_task_ident (RTEMS_SELF, 0, &tid); + rtems_event_system_receive (in, RTEMS_EVENT_ANY | RTEMS_NO_WAIT, RTEMS_NO_TIMEOUT, &out); + for (;;) { + rtems_bsdnet_semaphore_obtain (); + error = selscan(tid, ibits, obits, nfds, &retval); + rtems_bsdnet_semaphore_release (); + if (error || retval) + break; + if (timo) { + now = rtems_clock_get_ticks_since_boot(); + timo -= now - then; + if (timo <= 0) + break; + then = now; + } + rtems_event_system_receive (in, RTEMS_EVENT_ANY | RTEMS_WAIT, timo, &out); + } + +#define putbits(name,i) if (name) *name = ob[i] + putbits (readfds, 0); + putbits (writefds, 1); + putbits (exceptfds, 2); +#undef putbits + if (error) { + errno = error; + retval = -1; + } + return (retval); +} diff --git a/rtems/rtems_showicmpstat.c b/rtems/rtems_showicmpstat.c new file mode 100644 index 0000000..4862d6c --- /dev/null +++ b/rtems/rtems_showicmpstat.c @@ -0,0 +1,70 @@ +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * Display ICMP statistics + * Don't lock the rest of the network tasks out while printing. + * It's no big deal if the values change while being printed. + */ +static void +showicmpstat (const char *name, unsigned long n) +{ + if (n) + printf ("%35s%12lu\n", name, n); +} + +/* + * External data we peek at during statistics reporting + */ +extern unsigned int icmplenPanicAvoided; + +void +rtems_bsdnet_show_icmp_stats (void) +{ + int i; + char cbuf[20]; + + printf ("************ ICMP Statistics ************\n"); + showicmpstat ("Calls to icmp_error()", icmpstat.icps_error); + showicmpstat ("Errors not sent -- old was icmp", icmpstat.icps_oldicmp); + for (i = 0 ; i <= ICMP_MAXTYPE ; i++) { + if (icmpstat.icps_outhist[i]) { + sprintf (cbuf, "Type %d sent", i); + showicmpstat (cbuf, icmpstat.icps_outhist[i]); + } + } + showicmpstat ("icmp_code out of range", icmpstat.icps_badcode); + showicmpstat ("packet < ICMP_MINLEN", icmpstat.icps_tooshort); + showicmpstat ("bad checksum", icmpstat.icps_checksum); + showicmpstat ("calculated bound mismatch", icmpstat.icps_badlen); + showicmpstat ("number of responses", icmpstat.icps_reflect); + showicmpstat ("all echo requests dropped", icmpstat.icps_allecho); + showicmpstat ("b/mcast echo requests dropped", icmpstat.icps_bmcastecho); + showicmpstat ("b/mcast tstamp requests dropped", icmpstat.icps_bmcasttstamp); + for (i = 0 ; i <= ICMP_MAXTYPE ; i++) { + if (icmpstat.icps_inhist[i]) { + sprintf (cbuf, "Type %d received", i); + showicmpstat (cbuf, icmpstat.icps_inhist[i]); + } + } + showicmpstat ("ICMP panic avoided", icmplenPanicAvoided); + printf ("\n"); +} diff --git a/rtems/rtems_showifstat.c b/rtems/rtems_showifstat.c new file mode 100644 index 0000000..21f21f4 --- /dev/null +++ b/rtems/rtems_showifstat.c @@ -0,0 +1,157 @@ +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * Display an address + */ +static int +showaddress (char *name, struct sockaddr *a) +{ + struct sockaddr_in *sa; + char buf[17]; + + if (!a) + return 0; + printf ("%s:", name); + sa = (struct sockaddr_in *)a; + printf ("%-16s", inet_ntop (AF_INET, &sa->sin_addr, buf, sizeof(buf))); + return 1; +} + +/* + * Display interface statistics + */ +void +rtems_bsdnet_show_if_stats (void) +{ + struct ifnet *ifp; + struct ifaddr *ifa; + unsigned short bit, flags; + + printf ("************ INTERFACE STATISTICS ************\n"); + for (ifp = ifnet; ifp; ifp = ifp->if_next) { + printf ("***** %s%d *****\n", ifp->if_name, ifp->if_unit); + for (ifa = ifp->if_addrlist ; ifa ; ifa = ifa->ifa_next) { + + if ( !ifa->ifa_addr ) + continue; + + switch ( ifa->ifa_addr->sa_family ) { + case AF_LINK: + { + struct sockaddr_dl *sdl = (struct sockaddr_dl *)ifa->ifa_addr; + unsigned char *cp = (unsigned char *)LLADDR(sdl); + int i; + + switch ( sdl->sdl_type ) { + case IFT_ETHER: + if ( (i=sdl->sdl_alen) > 0 ) { + printf("Ethernet Address: "); + do { + i--; + printf("%02X%c", *cp++, i ? ':' : '\n'); + } while ( i>0 ); + } + break; + + default: + break; + } + } + break; + + case AF_INET: + { + int printed; + printed = showaddress ("Address", ifa->ifa_addr); + if (ifp->if_flags & IFF_BROADCAST) + printed |= showaddress ("Broadcast Address", ifa->ifa_broadaddr); + if (ifp->if_flags & IFF_POINTOPOINT) + printed |= showaddress ("Destination Address", ifa->ifa_dstaddr); + printed |= showaddress ("Net mask", ifa->ifa_netmask); + if (printed) + printf ("\n"); + } + break; + + default: + break; + } + } + + printf ("Flags:"); + for (bit = 1, flags = ifp->if_flags ; flags ; bit <<= 1) { + char *cp; + char xbuf[20]; + switch (flags & bit) { + case 0: cp = NULL; break; + case IFF_UP: cp = "Up"; break; + case IFF_BROADCAST: cp = "Broadcast"; break; + case IFF_DEBUG: cp = "Debug"; break; + case IFF_LOOPBACK: cp = "Loopback"; break; + case IFF_POINTOPOINT: cp = "Point-to-point"; break; + case IFF_RUNNING: cp = "Running"; break; + case IFF_NOARP: cp = "No-ARP"; break; + case IFF_PROMISC: cp = "Promiscuous"; break; + case IFF_ALLMULTI: cp = "All-multicast"; break; + case IFF_OACTIVE: cp = "Active"; break; + case IFF_SIMPLEX: cp = "Simplex"; break; + case IFF_LINK0: cp = "Link0"; break; + case IFF_LINK1: cp = "Link1"; break; + case IFF_LINK2: cp = "Link2"; break; + case IFF_MULTICAST: cp = "Multicast"; break; + default: sprintf (xbuf, "%#x", bit); cp = xbuf; break; + } + if (cp) { + flags &= ~bit; + printf (" %s", cp); + } + } + printf ("\n"); + + printf ("Send queue limit:%-4d length:%-4d Dropped:%-8d\n", + ifp->if_snd.ifq_maxlen, + ifp->if_snd.ifq_len, + ifp->if_snd.ifq_drops); + + /* + * FIXME: Could print if_data statistics here, + * but right now the drivers maintain their + * own statistics. + */ + + /* + * Grab the network semaphore. + * In most cases this is not necessary, but it's + * easier to always call the driver ioctl function + * while holding the semaphore than to try + * and explain why some ioctl commands are invoked + * while holding the semaphore and others are + * invoked while not holding the semaphore. + */ + rtems_bsdnet_semaphore_obtain (); + (*ifp->if_ioctl)(ifp, SIO_RTEMS_SHOW_STATS, NULL); + rtems_bsdnet_semaphore_release (); + } + printf ("\n"); +} diff --git a/rtems/rtems_showipstat.c b/rtems/rtems_showipstat.c new file mode 100644 index 0000000..5b30da5 --- /dev/null +++ b/rtems/rtems_showipstat.c @@ -0,0 +1,68 @@ +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * Display IP statistics + * Don't lock the rest of the network tasks out while printing. + * It's no big deal if the values change while being printed. + */ +static void +showipstat (const char *name, unsigned long n) +{ + if (n) + printf ("%35s%12lu\n", name, n); +} + +void +rtems_bsdnet_show_ip_stats (void) +{ + printf ("************ IP Statistics ************\n"); + showipstat ("total packets received", ipstat.ips_total); + showipstat ("checksum bad", ipstat.ips_badsum); + showipstat ("packet too short", ipstat.ips_tooshort); + showipstat ("not enough data", ipstat.ips_toosmall); + showipstat ("ip header length < data size", ipstat.ips_badhlen); + showipstat ("ip length < ip header length", ipstat.ips_badlen); + showipstat ("fragments received", ipstat.ips_fragments); + showipstat ("frags dropped (dups, out of space)", ipstat.ips_fragdropped); + showipstat ("fragments timed out", ipstat.ips_fragtimeout); + showipstat ("packets forwarded", ipstat.ips_forward); + showipstat ("packets rcvd for unreachable dest", ipstat.ips_cantforward); + showipstat ("packets forwarded on same net", ipstat.ips_redirectsent); + showipstat ("unknown or unsupported protocol", ipstat.ips_noproto); + showipstat ("datagrams delivered to upper level", ipstat.ips_delivered); + showipstat ("total ip packets generated here", ipstat.ips_localout); + showipstat ("lost packets due to nobufs, etc.", ipstat.ips_odropped); + showipstat ("total packets reassembled ok", ipstat.ips_reassembled); + showipstat ("datagrams successfully fragmented", ipstat.ips_fragmented); + showipstat ("output fragments created", ipstat.ips_ofragments); + showipstat ("don't fragment flag was set, etc.", ipstat.ips_cantfrag); + showipstat ("error in option processing", ipstat.ips_badoptions); + showipstat ("packets discarded due to no route", ipstat.ips_noroute); + showipstat ("ip version != 4", ipstat.ips_badvers); + showipstat ("total raw ip packets generated", ipstat.ips_rawout); + showipstat ("ip length > max ip packet size", ipstat.ips_toolong); + showipstat ("ip input queue drops", ipintrq.ifq_drops); + printf ("\n"); +} diff --git a/rtems/rtems_showmbuf.c b/rtems/rtems_showmbuf.c new file mode 100644 index 0000000..27f8cff --- /dev/null +++ b/rtems/rtems_showmbuf.c @@ -0,0 +1,69 @@ +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * Display MBUF statistics + * Don't lock the rest of the network tasks out while printing. + * It's no big deal if the values change while being printed. + */ +void +rtems_bsdnet_show_mbuf_stats (void) +{ + int i; + int printed = 0; + char *cp; + + printf ("************ MBUF STATISTICS ************\n"); + printf ("mbufs:%4lu clusters:%4lu free:%4lu\n", + mbstat.m_mbufs, mbstat.m_clusters, mbstat.m_clfree); + printf ("drops:%4lu waits:%4lu drains:%4lu\n", + mbstat.m_drops, mbstat.m_wait, mbstat.m_drain); + for (i = 0 ; i < 20 ; i++) { + switch (i) { + case MT_FREE: cp = "free"; break; + case MT_DATA: cp = "data"; break; + case MT_HEADER: cp = "header"; break; + case MT_SOCKET: cp = "socket"; break; + case MT_PCB: cp = "pcb"; break; + case MT_RTABLE: cp = "rtable"; break; + case MT_HTABLE: cp = "htable"; break; + case MT_ATABLE: cp = "atable"; break; + case MT_SONAME: cp = "soname"; break; + case MT_SOOPTS: cp = "soopts"; break; + case MT_FTABLE: cp = "ftable"; break; + case MT_RIGHTS: cp = "rights"; break; + case MT_IFADDR: cp = "ifaddr"; break; + case MT_CONTROL: cp = "control"; break; + case MT_OOBDATA: cp = "oobdata"; break; + default: cp = NULL; break; + } + if ((cp != NULL) || (mbstat.m_mtypes[i] != 0)) { + char cbuf[16]; + if (cp == NULL) { + sprintf (cbuf, "Type %d", i); + cp = cbuf; + } + printf ("%10s:%-8u", cp, mbstat.m_mtypes[i]); + if (++printed == 4) { + printf ("\n"); + printed = 0; + } + } + } + if (printed) + printf ("\n"); + printf ("\n"); +} diff --git a/rtems/rtems_showroute.c b/rtems/rtems_showroute.c new file mode 100644 index 0000000..971c364 --- /dev/null +++ b/rtems/rtems_showroute.c @@ -0,0 +1,240 @@ +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +/* + * We'll use the application versions of realloc and free. + */ +#undef free +#undef malloc +#undef random +#include + +#include + +/* + * Information per route + */ +struct rinfo { + struct sockaddr dst; + union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_dl sdl; + } un; + unsigned long pksent; + unsigned long expire; + int flags; + char ifname[16]; + short ifunit; + short refcnt; +}; + +/* + * Information per display + */ +struct dinfo { + int capacity; + int count; + struct rinfo *routes; +}; + +/* + * Copy address + */ +static void +copyAddress (void *to, void *from, int tolen) +{ + int ncopy; + struct sockaddr dummy; + + if (from == NULL) { + /* + * Create a fake address of unspecified type + */ + from = &dummy; + dummy.sa_len = 4; + dummy.sa_family = AF_UNSPEC; + } + ncopy = ((struct sockaddr *)from)->sa_len; + if (ncopy > tolen) + ncopy = tolen; + memcpy (to, from, ncopy); +} + +/* + * Package everything up before printing it. + * We don't want to block all network operations till + * the printing completes! + */ +static int +show_inet_route ( + struct radix_node *rn, + void *vw ) +{ + struct rtentry *rt = (struct rtentry *)rn; + struct ifnet *ifp; + struct dinfo *dp = (struct dinfo *)vw; + struct rinfo *r; + + /* + * Get a pointer to a new route info structure + */ + if (dp->count >= dp->capacity) { + r = realloc (dp->routes, (sizeof *r) * (dp->capacity + 20)); + if (r == 0) + return ENOMEM; + dp->capacity += 20; + dp->routes = r; + } + r = dp->routes + dp->count++; + + /* + * Fill in the route info structure + */ + copyAddress (&r->dst, rt_key(rt), sizeof r->dst); + if (rt->rt_flags & (RTF_GATEWAY | RTF_HOST)) { + copyAddress (&r->un, rt->rt_gateway, sizeof r->un); + } + else { + /* + * Create a fake address to hold the mask + */ + struct sockaddr_in dummy; + + dummy.sin_family = AF_INET; + dummy.sin_len = sizeof dummy; + dummy.sin_addr = ((struct sockaddr_in *)rt_mask(rt))->sin_addr; + copyAddress (&r->un, &dummy, sizeof r->un); + } + r->flags = rt->rt_flags; + r->refcnt = rt->rt_refcnt; + r->pksent = rt->rt_rmx.rmx_pksent; + r->expire = rt->rt_rmx.rmx_expire; + ifp = rt->rt_ifp; + strncpy (r->ifname, (ifp->if_name ? ifp->if_name : ""), sizeof r->ifname); + r->ifunit = ifp->if_unit; + return 0; +} + +/* + * Convert link address to ASCII + */ +static char * +link_ascii (struct sockaddr_dl *sdl, char *buf, int bufsize) +{ + char *cp; + int i; + int first = 1; + int nleft = sdl->sdl_alen; + char *ap = LLADDR (sdl); + static const char hextab[16] = "0123456789ABCDEF"; + + cp = buf; + while (nleft && (bufsize > 4)) { + if (first) { + first = 0; + } + else { + *cp++ = ':'; + bufsize--; + } + i = *ap++; + *cp++ = hextab[(i >> 4) & 0xf]; + *cp++ = hextab[i & 0xf]; + nleft--; + bufsize -= 2; + } + *cp = '\0'; + return buf; +} + +void +rtems_bsdnet_show_inet_routes (void) +{ + struct radix_node_head *rnh; + struct dinfo d; + struct rinfo *r; + int i, error; + + /* + * For now we'll handle only AF_INET + */ + rnh = rt_tables[AF_INET]; + if (!rnh) + return; + d.count = d.capacity = 0; + d.routes = NULL; + rtems_bsdnet_semaphore_obtain (); + error = rnh->rnh_walktree(rnh, show_inet_route, &d); + rtems_bsdnet_semaphore_release (); + if (error) { + printf ("Can't get route info: %s\n", strerror (error)); + return; + } + if (d.count == 0) { + printf ("No routes!\n"); + return; + } + printf ("Destination Gateway/Mask/Hw Flags Refs Use Expire Interface\n"); + for (i = 0, r = d.routes ; i < d.count ; i++, r++) { + char buf[30]; + char *cp, *fc, flagbuf[10]; + const char *addr; + unsigned long flagbit; + struct sockaddr_in *sin; + + sin = (struct sockaddr_in *)&r->dst; + if (sin->sin_addr.s_addr == INADDR_ANY) + addr = "default"; + else + addr = inet_ntop (AF_INET, &sin->sin_addr, buf, sizeof buf); + printf ("%-16s", addr); + switch (r->un.sa.sa_family) { + case AF_INET: + addr = inet_ntop (AF_INET, &r->un.sin.sin_addr, buf, sizeof buf); + break; + + case AF_LINK: + addr = link_ascii (&r->un.sdl, buf, sizeof buf); + break; + + default: + addr = ""; + break; + } + printf ("%-19s", addr); + fc = "UGHRDM XLS"; + for (flagbit = 0x1, cp = flagbuf ; *fc ; flagbit <<= 1, fc++) { + if ((r->flags & flagbit) && (*fc != ' ')) + *cp++ = *fc; + } + *cp = '\0'; + printf ("%-10s%3d%9ld%7ld %.*s%d\n", flagbuf, + r->refcnt, r->pksent, + r->expire, + (int)sizeof r->ifname, r->ifname, + r->ifunit); + } + free (d.routes); +} diff --git a/rtems/rtems_showtcpstat.c b/rtems/rtems_showtcpstat.c new file mode 100644 index 0000000..3a17809 --- /dev/null +++ b/rtems/rtems_showtcpstat.c @@ -0,0 +1,106 @@ +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * Display TCP statistics + * Don't lock the rest of the network tasks out while printing. + * It's no big deal if the values change while being printed. + */ +static void +showtcpstat (const char *name, unsigned long n) +{ + if (n) + printf ("%35s%12lu\n", name, n); +} + +void +rtems_bsdnet_show_tcp_stats (void) +{ + printf ("************ TCP Statistics ************\n"); + showtcpstat ("connections initiated", tcpstat.tcps_connattempt); + showtcpstat ("connections accepted", tcpstat.tcps_accepts); + showtcpstat ("connections established", tcpstat.tcps_connects); + showtcpstat ("connections dropped", tcpstat.tcps_drops); + showtcpstat ("embryonic connections dropped", tcpstat.tcps_conndrops); + showtcpstat ("conn. closed (includes drops)", tcpstat.tcps_closed); + showtcpstat ("segs where we tried to get rtt", tcpstat.tcps_segstimed); + showtcpstat ("times we succeeded", tcpstat.tcps_rttupdated); + showtcpstat ("delayed acks sent", tcpstat.tcps_delack); + showtcpstat ("conn. dropped in rxmt timeout", tcpstat.tcps_timeoutdrop); + showtcpstat ("retransmit timeouts", tcpstat.tcps_rexmttimeo); + showtcpstat ("persist timeouts", tcpstat.tcps_persisttimeo); + showtcpstat ("keepalive timeouts", tcpstat.tcps_keeptimeo); + showtcpstat ("keepalive probes sent", tcpstat.tcps_keepprobe); + showtcpstat ("connections dropped in keepalive", tcpstat.tcps_keepdrops); + + showtcpstat ("total packets sent", tcpstat.tcps_sndtotal); + showtcpstat ("data packets sent", tcpstat.tcps_sndpack); + showtcpstat ("data bytes sent", tcpstat.tcps_sndbyte); + showtcpstat ("data packets retransmitted", tcpstat.tcps_sndrexmitpack); + showtcpstat ("data bytes retransmitted", tcpstat.tcps_sndrexmitbyte); + showtcpstat ("ack-only packets sent", tcpstat.tcps_sndacks); + showtcpstat ("window probes sent", tcpstat.tcps_sndprobe); + showtcpstat ("packets sent with URG only", tcpstat.tcps_sndurg); + showtcpstat ("window update-only packets sent", tcpstat.tcps_sndwinup); + showtcpstat ("control (SYN|FIN|RST) packets sent", tcpstat.tcps_sndctrl); + + showtcpstat ("total packets received", tcpstat.tcps_rcvtotal); + showtcpstat ("packets received in sequence", tcpstat.tcps_rcvpack); + showtcpstat ("bytes received in sequence", tcpstat.tcps_rcvbyte); + showtcpstat ("packets received with ccksum errs", tcpstat.tcps_rcvbadsum); + showtcpstat ("packets received with bad offset", tcpstat.tcps_rcvbadoff); + showtcpstat ("packets received too short", tcpstat.tcps_rcvshort); + showtcpstat ("duplicate-only packets received", tcpstat.tcps_rcvduppack); + showtcpstat ("duplicate-only bytes received", tcpstat.tcps_rcvdupbyte); + showtcpstat ("packets with some duplicate data", tcpstat.tcps_rcvpartduppack); + showtcpstat ("dup. bytes in part-dup. packets", tcpstat.tcps_rcvpartdupbyte); + showtcpstat ("out-of-order packets received", tcpstat.tcps_rcvoopack); + showtcpstat ("out-of-order bytes received", tcpstat.tcps_rcvoobyte); + showtcpstat ("packets with data after window", tcpstat.tcps_rcvpackafterwin); + showtcpstat ("bytes rcvd after window", tcpstat.tcps_rcvbyteafterwin); + showtcpstat ("packets rcvd after \"close\"", tcpstat.tcps_rcvafterclose); + showtcpstat ("rcvd window probe packets", tcpstat.tcps_rcvwinprobe); + showtcpstat ("rcvd duplicate acks", tcpstat.tcps_rcvdupack); + showtcpstat ("rcvd acks for unsent data", tcpstat.tcps_rcvacktoomuch); + showtcpstat ("rcvd ack packets", tcpstat.tcps_rcvackpack); + showtcpstat ("bytes acked by rcvd acks", tcpstat.tcps_rcvackbyte); + showtcpstat ("rcvd window update packets", tcpstat.tcps_rcvwinupd); + showtcpstat ("segments dropped due to PAWS", tcpstat.tcps_pawsdrop); + showtcpstat ("times hdr predict ok for acks", tcpstat.tcps_predack); + showtcpstat ("times hdr predict ok for data pkts", tcpstat.tcps_preddat); + showtcpstat ("pcb cache misses", tcpstat.tcps_pcbcachemiss); + showtcpstat ("times cached RTT in route updated", tcpstat.tcps_cachedrtt); + showtcpstat ("times cached rttvar updated", tcpstat.tcps_cachedrttvar); + showtcpstat ("times cached ssthresh updated", tcpstat.tcps_cachedssthresh); + showtcpstat ("times RTT initialized from route", tcpstat.tcps_usedrtt); + showtcpstat ("times RTTVAR initialized from rt", tcpstat.tcps_usedrttvar); + showtcpstat ("times ssthresh initialized from rt", tcpstat.tcps_usedssthresh); + showtcpstat ("timeout in persist state", tcpstat.tcps_persistdrop); + showtcpstat ("bogus SYN, e.g. premature ACK", tcpstat.tcps_badsyn); + showtcpstat ("resends due to MTU discovery", tcpstat.tcps_mturesent); + showtcpstat ("listen queue overflows", tcpstat.tcps_listendrop); + printf ("\n"); +} diff --git a/rtems/rtems_showudpstat.c b/rtems/rtems_showudpstat.c new file mode 100644 index 0000000..c6b4560 --- /dev/null +++ b/rtems/rtems_showudpstat.c @@ -0,0 +1,52 @@ +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * Display UDP statistics + * Don't lock the rest of the network tasks out while printing. + * It's no big deal if the values change while being printed. + */ +static void +showudpstat (const char *name, unsigned long n) +{ + if (n) + printf ("%35s%12lu\n", name, n); +} + +void +rtems_bsdnet_show_udp_stats (void) +{ + printf ("************ UDP Statistics ************\n"); + showudpstat ("total input packets", udpstat.udps_ipackets); + showudpstat ("packet shorter than header", udpstat.udps_hdrops); + showudpstat ("checksum error", udpstat.udps_badsum); + showudpstat ("data length larger than packet", udpstat.udps_badlen); + showudpstat ("no socket on port", udpstat.udps_noport); + showudpstat ("of above, arrived as broadcast", udpstat.udps_noportbcast); + showudpstat ("not delivered, input socket full", udpstat.udps_fullsock); + showudpstat ("input packets missing pcb cache", udpstat.udpps_pcbcachemiss); + showudpstat ("input packets not for hashed pcb", udpstat.udpps_pcbhashmiss); + showudpstat ("total output packets", udpstat.udps_opackets); + printf ("\n"); +} diff --git a/rtems/rtems_socketpair.c b/rtems/rtems_socketpair.c new file mode 100644 index 0000000..1dcaf6e --- /dev/null +++ b/rtems/rtems_socketpair.c @@ -0,0 +1,53 @@ +#include + +/* + * socketpair() for RTEMS + * + * This file exists primarily to document what is required to provide + * a functional implementation of socketpair() for RTEMS. + * + * The socketpair() service requires that the "local domain" sockets + * be functional. This is denoted by the domain constants AF_LOCAL + * and AF_UNIX and the protocol constants PF_LOCAL and PF_UNIX. The + * local domain functionality is implemented in the file kern/uipc_usrreq.c + * which was not part of the initial port of the FreeBSD stack to + * RTEMS. + * + * The FreeBSD socketpair implementation appears to be dependent on + * file system features which are not available currently in RTEMS. + * + * COPYRIGHT (c) 1989-2007. + * On-Line Applications Research Corporation (OAR). + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "rtems_syscall.h" + +int socketpair (int domain, int type, int protocol, int *rsv) +{ + if ( !rsv ) { + errno = EFAULT; + return -1; + } + + /* + * Yes, we do not support socketpair() so this is really paranoid. + * But it ensures that someone calling this routine and ignoring + * the return status will get errors from subsequent socket calls. + */ + rsv[ 0 ] = -1; + rsv[ 1 ] = -1; + errno = ENOSYS; + return -1; +} diff --git a/rtems/rtems_syscall.c b/rtems/rtems_syscall.c new file mode 100644 index 0000000..7f3143b --- /dev/null +++ b/rtems/rtems_syscall.c @@ -0,0 +1,835 @@ +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +/* #include */ +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "rtems_syscall.h" + +/* + * Hooks to RTEMS I/O system + */ +static const rtems_filesystem_file_handlers_r socket_handlers; + +/* + * Convert an RTEMS file descriptor to a BSD socket pointer. + */ +struct socket * +rtems_bsdnet_fdToSocket (int fd) +{ + rtems_libio_t *iop; + + if ((uint32_t)fd >= rtems_libio_number_iops) { + errno = EBADF; + return NULL; + } + + iop = rtems_libio_iop(fd); + + if ((rtems_libio_iop_flags(iop) & LIBIO_FLAGS_OPEN) == 0) { + errno = EBADF; + return NULL; + } + + if (iop->pathinfo.handlers != &socket_handlers) { + errno = ENOTSOCK; + return NULL; + } + + if (iop->data1 == NULL) + errno = EBADF; + return iop->data1; +} + +/* + * Create an RTEMS file descriptor for a socket + */ +static int +rtems_bsdnet_makeFdForSocket (void *so) +{ + rtems_libio_t *iop; + int fd; + + iop = rtems_libio_allocate(); + if (iop == 0) + rtems_set_errno_and_return_minus_one( ENFILE ); + + fd = rtems_libio_iop_to_descriptor(iop); + iop->data0 = fd; + iop->data1 = so; + iop->pathinfo.handlers = &socket_handlers; + iop->pathinfo.mt_entry = &rtems_filesystem_null_mt_entry; + rtems_filesystem_location_add_to_mt_entry(&iop->pathinfo); + rtems_libio_iop_flags_set(iop, LIBIO_FLAGS_OPEN | LIBIO_FLAGS_READ_WRITE); + return fd; +} + +/* + * Package system call argument into mbuf. + */ +static int +sockargstombuf (struct mbuf **mp, const void *buf, int buflen, int type) +{ + struct mbuf *m; + + if ((u_int)buflen > MLEN) + return (EINVAL); + m = m_get(M_WAIT, type); + if (m == NULL) + return (ENOBUFS); + m->m_len = buflen; + memcpy (mtod(m, caddr_t), buf, buflen); + *mp = m; + if (type == MT_SONAME) { + struct sockaddr *sa; + sa = mtod(m, struct sockaddr *); + sa->sa_len = buflen; + } + return 0; +} + +/* + ********************************************************************* + * BSD-style entry points * + ********************************************************************* + */ +int +socket (int domain, int type, int protocol) +{ + int fd; + int error; + struct socket *so; + + rtems_bsdnet_semaphore_obtain (); + error = socreate(domain, &so, type, protocol, NULL); + if (error == 0) { + fd = rtems_bsdnet_makeFdForSocket (so); + if (fd < 0) + soclose (so); + } + else { + errno = error; + fd = -1; + } + rtems_bsdnet_semaphore_release (); + return fd; +} + +int +bind (int s, const struct sockaddr *name, socklen_t namelen) +{ + int error; + int ret = -1; + struct socket *so; + struct mbuf *nam; + + rtems_bsdnet_semaphore_obtain (); + if ((so = rtems_bsdnet_fdToSocket (s)) != NULL) { + error = sockargstombuf (&nam, name, namelen, MT_SONAME); + if (error == 0) { + error = sobind (so, nam); + if (error == 0) + ret = 0; + else + errno = error; + m_freem (nam); + } + else { + errno = error; + } + } + rtems_bsdnet_semaphore_release (); + return ret; +} + +int +connect (int s, const struct sockaddr *name, socklen_t namelen) +{ + int error; + int ret = -1; + struct socket *so; + struct mbuf *nam; + + rtems_bsdnet_semaphore_obtain (); + if ((so = rtems_bsdnet_fdToSocket (s)) == NULL) { + rtems_bsdnet_semaphore_release (); + return -1; + } + if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) { + errno = EALREADY; + rtems_bsdnet_semaphore_release (); + return -1; + } + error = sockargstombuf (&nam, name, namelen, MT_SONAME); + if (error) { + errno = error; + rtems_bsdnet_semaphore_release (); + return -1; + } + error = soconnect (so, nam); + if (error) + goto bad; + if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) { + m_freem(nam); + errno = EINPROGRESS; + rtems_bsdnet_semaphore_release (); + return -1; + } + error = so->so_error; + while (error == 0 && (so->so_state & SS_ISCONNECTING)) { + error = soconnsleep (so); + if (error) + break; + error = so->so_error; + so->so_error = 0; + } + bad: + if (error != ENXIO) + so->so_state &= ~SS_ISCONNECTING; + m_freem (nam); + if (error) + errno = error; + else + ret = 0; + rtems_bsdnet_semaphore_release (); + return ret; +} + +int +listen (int s, int backlog) +{ + int error; + int ret = -1; + struct socket *so; + + rtems_bsdnet_semaphore_obtain (); + if ((so = rtems_bsdnet_fdToSocket (s)) != NULL) { + error = solisten (so, backlog); + if (error == 0) + ret = 0; + else + errno = error; + } + rtems_bsdnet_semaphore_release (); + return ret; +} + +int +accept (int s, struct sockaddr *name, socklen_t *namelen) +{ + int fd; + struct socket *head, *so; + struct mbuf *nam; + int error; + + rtems_bsdnet_semaphore_obtain (); + if ((head = rtems_bsdnet_fdToSocket (s)) == NULL) { + rtems_bsdnet_semaphore_release (); + return -1; + } + if ((head->so_options & SO_ACCEPTCONN) == 0) { + errno = EINVAL; + rtems_bsdnet_semaphore_release (); + return -1; + } + if ((head->so_state & SS_NBIO) && head->so_comp.tqh_first == NULL) { + errno = EWOULDBLOCK; + rtems_bsdnet_semaphore_release (); + return -1; + } + error = head->so_error; + while (error == 0 && head->so_comp.tqh_first == NULL) { + if (head->so_state & SS_CANTRCVMORE) { + error = ECONNABORTED; + break; + } + error = soconnsleep (head); + } + if (error) { + errno = error; + rtems_bsdnet_semaphore_release (); + return -1; + } + + so = head->so_comp.tqh_first; + TAILQ_REMOVE(&head->so_comp, so, so_list); + head->so_qlen--; + + fd = rtems_bsdnet_makeFdForSocket (so); + if (fd < 0) { + TAILQ_INSERT_HEAD(&head->so_comp, so, so_list); + head->so_qlen++; + soconnwakeup (head); + rtems_bsdnet_semaphore_release (); + return -1; + } + so->so_state &= ~SS_COMP; + so->so_head = NULL; + + nam = m_get(M_WAIT, MT_SONAME); + (void) soaccept(so, nam); + if (name) { + /* check length before it is destroyed */ + if (*namelen > nam->m_len) + *namelen = nam->m_len; + memcpy (name, mtod(nam, caddr_t), *namelen); + } + m_freem(nam); + rtems_bsdnet_semaphore_release (); + return (fd); + +} + +/* + * Shutdown routine + */ + +int +shutdown (int s, int how) +{ + struct socket *so; + int error; + + rtems_bsdnet_semaphore_obtain (); + if ((so = rtems_bsdnet_fdToSocket (s)) == NULL) { + rtems_bsdnet_semaphore_release (); + return -1; + } + error = soshutdown(so, how); + rtems_bsdnet_semaphore_release (); + if (error) { + errno = error; + return -1; + } + return 0; +} + +/* + * All `transmit' operations end up calling this routine. + */ +ssize_t +sendmsg (int s, const struct msghdr *mp, int flags) +{ + int ret = -1; + int error; + struct uio auio; + struct iovec *iov; + struct socket *so; + struct mbuf *to; + struct mbuf *control = NULL; + int i; + int len; + + rtems_bsdnet_semaphore_obtain (); + if ((so = rtems_bsdnet_fdToSocket (s)) == NULL) { + rtems_bsdnet_semaphore_release (); + return -1; + } + auio.uio_iov = mp->msg_iov; + auio.uio_iovcnt = mp->msg_iovlen; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_rw = UIO_WRITE; + auio.uio_offset = 0; + auio.uio_resid = 0; + iov = mp->msg_iov; + for (i = 0; i < mp->msg_iovlen; i++, iov++) { + if ((auio.uio_resid += iov->iov_len) < 0) { + errno = EINVAL; + rtems_bsdnet_semaphore_release (); + return -1; + } + } + if (mp->msg_name) { + error = sockargstombuf (&to, mp->msg_name, mp->msg_namelen, MT_SONAME); + if (error) { + errno = error; + rtems_bsdnet_semaphore_release (); + return -1; + } + } + else { + to = NULL; + } + if (mp->msg_control) { + if (mp->msg_controllen < sizeof (struct cmsghdr)) { + errno = EINVAL; + if (to) + m_freem(to); + rtems_bsdnet_semaphore_release (); + return -1; + } + sockargstombuf (&control, mp->msg_control, mp->msg_controllen, MT_CONTROL); + } + else { + control = NULL; + } + len = auio.uio_resid; + error = sosend (so, to, &auio, (struct mbuf *)0, control, flags); + if (error) { + if (auio.uio_resid != len && (error == EINTR || error == EWOULDBLOCK)) + error = 0; + } + if (error) + errno = error; + else + ret = len - auio.uio_resid; + if (to) + m_freem(to); + rtems_bsdnet_semaphore_release (); + return (ret); +} + +/* + * Send a message to a host + */ +ssize_t +sendto (int s, const void *buf, size_t buflen, int flags, const struct sockaddr *to, socklen_t tolen) +{ + struct msghdr msg; + struct iovec iov; + + iov.iov_base = (void *)buf; + iov.iov_len = buflen; + msg.msg_name = (caddr_t)to; + msg.msg_namelen = tolen; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + return sendmsg (s, &msg, flags); +} + +/* + * All `receive' operations end up calling this routine. + */ +ssize_t +recvmsg (int s, struct msghdr *mp, int flags) +{ + int ret = -1; + int error; + struct uio auio; + struct iovec *iov; + struct socket *so; + struct mbuf *from = NULL, *control = NULL; + int i; + int len; + + rtems_bsdnet_semaphore_obtain (); + if ((so = rtems_bsdnet_fdToSocket (s)) == NULL) { + rtems_bsdnet_semaphore_release (); + return -1; + } + auio.uio_iov = mp->msg_iov; + auio.uio_iovcnt = mp->msg_iovlen; + auio.uio_segflg = UIO_USERSPACE; + auio.uio_rw = UIO_READ; + auio.uio_offset = 0; + auio.uio_resid = 0; + iov = mp->msg_iov; + for (i = 0; i < mp->msg_iovlen; i++, iov++) { + if ((auio.uio_resid += iov->iov_len) < 0) { + errno = EINVAL; + rtems_bsdnet_semaphore_release (); + return -1; + } + } + len = auio.uio_resid; + mp->msg_flags = flags; + error = soreceive (so, &from, &auio, (struct mbuf **)NULL, + mp->msg_control ? &control : (struct mbuf **)NULL, + &mp->msg_flags); + if (error) { + if (auio.uio_resid != len && (error == EINTR || error == EWOULDBLOCK)) + error = 0; + } + if (error) { + errno = error; + } + else { + ret = len - auio.uio_resid; + if (mp->msg_name) { + len = mp->msg_namelen; + if ((len <= 0) || (from == NULL)) { + len = 0; + } + else { + if (len > from->m_len) + len = from->m_len; + memcpy (mp->msg_name, mtod(from, caddr_t), len); + } + mp->msg_namelen = len; + } + if (mp->msg_control) { + struct mbuf *m; + void *ctlbuf; + + len = mp->msg_controllen; + m = control; + mp->msg_controllen = 0; + ctlbuf = mp->msg_control; + + while (m && (len > 0)) { + unsigned int tocopy; + + if (len >= m->m_len) + tocopy = m->m_len; + else { + mp->msg_flags |= MSG_CTRUNC; + tocopy = len; + } + memcpy(ctlbuf, mtod(m, caddr_t), tocopy); + ctlbuf += tocopy; + len -= tocopy; + m = m->m_next; + } + mp->msg_controllen = ctlbuf - mp->msg_control; + } + } + if (from) + m_freem (from); + if (control) + m_freem (control); + rtems_bsdnet_semaphore_release (); + return (ret); +} + +/* + * Receive a message from a host + */ +ssize_t +recvfrom (int s, void *buf, size_t buflen, int flags, struct sockaddr *from, socklen_t *fromlen) +{ + struct msghdr msg; + struct iovec iov; + int ret; + + iov.iov_base = buf; + iov.iov_len = buflen; + msg.msg_name = (caddr_t)from; + if (fromlen) + msg.msg_namelen = *fromlen; + else + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = NULL; + msg.msg_controllen = 0; + ret = recvmsg (s, &msg, flags); + if ((from != NULL) && (fromlen != NULL) && (ret >= 0)) + *fromlen = msg.msg_namelen; + return ret; +} + +int +setsockopt (int s, int level, int name, const void *val, socklen_t len) +{ + struct socket *so; + struct mbuf *m = NULL; + int error; + + rtems_bsdnet_semaphore_obtain (); + if ((so = rtems_bsdnet_fdToSocket (s)) == NULL) { + rtems_bsdnet_semaphore_release (); + return -1; + } + if (len > MLEN) { + errno = EINVAL; + rtems_bsdnet_semaphore_release (); + return -1; + } + if (val) { + error = sockargstombuf (&m, val, len, MT_SOOPTS); + if (error) { + errno = error; + rtems_bsdnet_semaphore_release (); + return -1; + } + } + error = sosetopt(so, level, name, m); + if (error) { + errno = error; + rtems_bsdnet_semaphore_release (); + return -1; + } + rtems_bsdnet_semaphore_release (); + return 0; +} + +int +getsockopt (int s, int level, int name, void *aval, socklen_t *avalsize) +{ + struct socket *so; + struct mbuf *m = NULL, *m0; + char *val = aval; + int i, op, valsize; + int error; + + rtems_bsdnet_semaphore_obtain (); + if ((so = rtems_bsdnet_fdToSocket (s)) == NULL) { + rtems_bsdnet_semaphore_release (); + return -1; + } + if (val) + valsize = *avalsize; + else + valsize = 0; + if (((error = sogetopt(so, level, name, &m)) == 0) && val && valsize && m) { + op = 0; + while (m && op < valsize) { + i = valsize - op; + if (i > m->m_len) + i = m->m_len; + memcpy (val, mtod(m, caddr_t), i); + op += i; + val += i; + m0 = m; + MFREE (m0, m); + } + *avalsize = op; + } + if (m != NULL) + (void) m_free(m); + if (error) { + errno = error; + rtems_bsdnet_semaphore_release (); + return -1; + } + rtems_bsdnet_semaphore_release (); + return 0; +} + +static int +getpeersockname (int s, struct sockaddr *name, socklen_t *namelen, int pflag) +{ + struct socket *so; + struct mbuf *m; + int len = *namelen; + int error; + + rtems_bsdnet_semaphore_obtain (); + if ((so = rtems_bsdnet_fdToSocket (s)) == NULL) { + rtems_bsdnet_semaphore_release (); + return -1; + } + m = m_getclr(M_WAIT, MT_SONAME); + if (m == NULL) { + errno = ENOBUFS; + rtems_bsdnet_semaphore_release (); + return -1; + } + if (pflag) + error = (*so->so_proto->pr_usrreqs->pru_peeraddr)(so, m); + else + error = (*so->so_proto->pr_usrreqs->pru_sockaddr)(so, m); + if (error) { + m_freem(m); + errno = error; + rtems_bsdnet_semaphore_release (); + return -1; + } + if (len > m->m_len) { + len = m->m_len; + *namelen = len; + } + memcpy (name, mtod(m, caddr_t), len); + m_freem (m); + rtems_bsdnet_semaphore_release (); + return 0; +} + +int +getpeername (int s, struct sockaddr *name, socklen_t *namelen) +{ + return getpeersockname (s, name, namelen, 1); +} +int +getsockname (int s, struct sockaddr *name, socklen_t *namelen) +{ + return getpeersockname (s, name, namelen, 0); +} + +int +sysctl(const int *name, u_int namelen, void *oldp, + size_t *oldlenp, const void *newp, size_t newlen) +{ + int error; + size_t j; + + rtems_bsdnet_semaphore_obtain (); + error = userland_sysctl (0, name, namelen, oldp, oldlenp, 1, newp, newlen, &j); + rtems_bsdnet_semaphore_release (); + + if (oldlenp) + *oldlenp = j; + + if (error) + { + errno = error; + return -1; + } + return 0; +} + +/* + ************************************************************************ + * RTEMS I/O HANDLER ROUTINES * + ************************************************************************ + */ +static int +rtems_bsdnet_close (rtems_libio_t *iop) +{ + struct socket *so; + int error; + + rtems_bsdnet_semaphore_obtain (); + if ((so = iop->data1) == NULL) { + errno = EBADF; + rtems_bsdnet_semaphore_release (); + return -1; + } + iop->data1 = NULL; + error = soclose (so); + rtems_bsdnet_semaphore_release (); + if (error) { + errno = error; + return -1; + } + return 0; +} + +static ssize_t +rtems_bsdnet_read (rtems_libio_t *iop, void *buffer, size_t count) +{ + return recv (iop->data0, buffer, count, 0); +} + +static ssize_t +rtems_bsdnet_write (rtems_libio_t *iop, const void *buffer, size_t count) +{ + return send (iop->data0, buffer, count, 0); +} + +static int +so_ioctl (rtems_libio_t *iop, struct socket *so, uint32_t command, void *buffer) +{ + unsigned int nonblock; + + switch (command) { + case FIONBIO: + nonblock = rtems_libio_fcntl_flags (O_NONBLOCK); + + if (*(int *)buffer) { + rtems_libio_iop_flags_set (iop, nonblock); + so->so_state |= SS_NBIO; + } + else { + rtems_libio_iop_flags_clear (iop, nonblock); + so->so_state &= ~SS_NBIO; + } + return 0; + + case FIONREAD: + *(int *)buffer = so->so_rcv.sb_cc; + return 0; + } + + if (IOCGROUP(command) == 'i') + return ifioctl (so, command, buffer, NULL); + if (IOCGROUP(command) == 'r') + return rtioctl (command, buffer, NULL); + return (*so->so_proto->pr_usrreqs->pru_control)(so, command, buffer, 0); +} + +static int +rtems_bsdnet_ioctl (rtems_libio_t *iop, ioctl_command_t command, void *buffer) +{ + struct socket *so; + int error; + + rtems_bsdnet_semaphore_obtain (); + if ((so = iop->data1) == NULL) { + errno = EBADF; + rtems_bsdnet_semaphore_release (); + return -1; + } + error = so_ioctl (iop, so, command, buffer); + rtems_bsdnet_semaphore_release (); + if (error) { + errno = error; + return -1; + } + return 0; +} + +static int +rtems_bsdnet_fcntl (rtems_libio_t *iop, int cmd) +{ + struct socket *so; + + if (cmd == F_SETFL) { + rtems_bsdnet_semaphore_obtain (); + if ((so = iop->data1) == NULL) { + rtems_bsdnet_semaphore_release (); + return EBADF; + } + if (rtems_libio_iop_is_no_delay(iop)) + so->so_state |= SS_NBIO; + else + so->so_state &= ~SS_NBIO; + rtems_bsdnet_semaphore_release (); + } + return 0; +} + +static int +rtems_bsdnet_fstat (const rtems_filesystem_location_info_t *loc, struct stat *sp) +{ + sp->st_mode = S_IFSOCK; + return 0; +} + +static const rtems_filesystem_file_handlers_r socket_handlers = { + .open_h = rtems_filesystem_default_open, + .close_h = rtems_bsdnet_close, + .read_h = rtems_bsdnet_read, + .write_h = rtems_bsdnet_write, + .ioctl_h = rtems_bsdnet_ioctl, + .lseek_h = rtems_filesystem_default_lseek, + .fstat_h = rtems_bsdnet_fstat, + .ftruncate_h = rtems_filesystem_default_ftruncate, + .fsync_h = rtems_filesystem_default_fsync_or_fdatasync, + .fdatasync_h = rtems_filesystem_default_fsync_or_fdatasync, + .fcntl_h = rtems_bsdnet_fcntl, + .kqfilter_h = rtems_filesystem_default_kqfilter, + .mmap_h = rtems_filesystem_default_mmap, + .poll_h = rtems_filesystem_default_poll, + .readv_h = rtems_filesystem_default_readv, + .writev_h = rtems_filesystem_default_writev +}; diff --git a/rtems/rtems_syscall.h b/rtems/rtems_syscall.h new file mode 100644 index 0000000..b0d38a6 --- /dev/null +++ b/rtems/rtems_syscall.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2016 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#ifndef _LIBNETWORKING_RTEMS_SYSCALL_H_ +#define _LIBNETWORKING_RTEMS_SYSCALL_H_ + +#include +#include +#include +#include +#include + +__BEGIN_DECLS + +int select(int, fd_set *, fd_set *, fd_set *, struct timeval *); + +int accept(int, struct sockaddr * __restrict, socklen_t * __restrict); + +int bind(int, const struct sockaddr *, socklen_t); + +int connect(int, const struct sockaddr *, socklen_t); + +int getpeername(int, struct sockaddr * __restrict, socklen_t * __restrict); + +int getsockname(int, struct sockaddr * __restrict, socklen_t * __restrict); + +int getsockopt(int, int, int, void * __restrict, socklen_t * __restrict); + +int listen(int, int); + +ssize_t recv(int, void *, size_t, int); + +ssize_t recvfrom(int, void *, size_t, int, struct sockaddr * __restrict, socklen_t * __restrict); + +ssize_t recvmsg(int, struct msghdr *, int); + +ssize_t send(int, const void *, size_t, int); + +ssize_t sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t); + +ssize_t sendmsg(int, const struct msghdr *, int); + +int setsockopt(int, int, int, const void *, socklen_t); + +int shutdown(int, int); + +int socket(int, int, int); + +int socketpair(int, int, int, int *); + +int sysctl(const int *, u_int, void *, size_t *, const void *, size_t); + +int sysctlbyname(const char *, void *, size_t *, const void *, size_t); + +int sysctlnametomib(const char *, int *, size_t *); + +__END_DECLS + +#endif /* _LIBNETWORKING_RTEMS_SYSCALL_H_ */ diff --git a/rtems/rtems_syscall_api.c b/rtems/rtems_syscall_api.c new file mode 100644 index 0000000..6899eba --- /dev/null +++ b/rtems/rtems_syscall_api.c @@ -0,0 +1,22 @@ +#include + +/* + * Copyright (c) 2016 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +/* Ensure that kernel and user space system call protoypes are identical */ +#include "rtems_syscall.h" diff --git a/rtems/sghostname.c b/rtems/sghostname.c new file mode 100644 index 0000000..df7513d --- /dev/null +++ b/rtems/sghostname.c @@ -0,0 +1,54 @@ +#include + +/* + * RTEMS versions of hostname functions + * FIXME: Not thread-safe + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +static char *rtems_hostname; + +int +gethostname (char *name, size_t namelen) +{ + char *cp = rtems_hostname; + + if (cp == NULL) + cp = ""; + strncpy (name, cp, namelen); + return 0; +} + +int +sethostname (const char *name, size_t namelen) +{ + char *old, *new; + + if (namelen >= MAXHOSTNAMELEN) { + errno = EINVAL; + return -1; + } + new = malloc (namelen + 1, M_HTABLE, M_NOWAIT); + if (new == NULL) { + errno = ENOMEM; + return -1; + } + strncpy (new, name, namelen); + new[namelen] = '\0'; + old = rtems_hostname; + rtems_hostname = new; + if (old) + free (old, M_HTABLE); + return 0; +} -- cgit v1.2.3