Before git init

This commit is contained in:
2022-12-24 14:06:57 +01:00
parent e357914e0c
commit 414a6bdd1a
422 changed files with 306837 additions and 2 deletions

View File

@@ -0,0 +1,563 @@
/**
* @file
* lwIP netif implementing an IEEE 802.1D MAC Bridge
*/
/*
* Copyright (c) 2017 Simon Goldschmidt.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Simon Goldschmidt <goldsimon@gmx.de>
*
*/
/**
* @defgroup bridgeif IEEE 802.1D bridge
* @ingroup netifs
* This file implements an IEEE 802.1D bridge by using a multilayer netif approach
* (one hardware-independent netif for the bridge that uses hardware netifs for its ports).
* On transmit, the bridge selects the outgoing port(s).
* On receive, the port netif calls into the bridge (via its netif->input function) and
* the bridge selects the port(s) (and/or its netif->input function) to pass the received pbuf to.
*
* Usage:
* - add the port netifs just like you would when using them as dedicated netif without a bridge
* - only NETIF_FLAG_ETHARP/NETIF_FLAG_ETHERNET netifs are supported as bridge ports
* - add the bridge port netifs without IPv4 addresses (i.e. pass 'NULL, NULL, NULL')
* - don't add IPv6 addresses to the port netifs!
* - set up the bridge configuration in a global variable of type 'bridgeif_initdata_t' that contains
* - the MAC address of the bridge
* - some configuration options controlling the memory consumption (maximum number of ports
* and FDB entries)
* - e.g. for a bridge MAC address 00-01-02-03-04-05, 2 bridge ports, 1024 FDB entries + 16 static MAC entries:
* bridgeif_initdata_t mybridge_initdata = BRIDGEIF_INITDATA1(2, 1024, 16, ETH_ADDR(0, 1, 2, 3, 4, 5));
* - add the bridge netif (with IPv4 config):
* struct netif bridge_netif;
* netif_add(&bridge_netif, &my_ip, &my_netmask, &my_gw, &mybridge_initdata, bridgeif_init, tcpip_input);
* NOTE: the passed 'input' function depends on BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT setting,
* which controls where the forwarding is done (netif low level input context vs. tcpip_thread)
* - set up all ports netifs and the bridge netif
*
* - When adding a port netif, NETIF_FLAG_ETHARP flag will be removed from a port
* to prevent ETHARP working on that port netif (we only want one IP per bridge not per port).
* - When adding a port netif, its input function is changed to call into the bridge.
*
*
* @todo:
* - compact static FDB entries (instead of walking the whole array)
* - add FDB query/read access
* - add FDB change callback (when learning or dropping auto-learned entries)
* - prefill FDB with MAC classes that should never be forwarded
* - multicast snooping? (and only forward group addresses to interested ports)
* - support removing ports
* - check SNMP integration
* - VLAN handling / trunk ports
* - priority handling? (although that largely depends on TX queue limitations and lwIP doesn't provide tx-done handling)
*/
#include "netif/bridgeif.h"
#include "lwip/netif.h"
#include "lwip/sys.h"
#include "lwip/etharp.h"
#include "lwip/ethip6.h"
#include "lwip/snmp.h"
#include "lwip/timeouts.h"
#include <string.h>
#if LWIP_NUM_NETIF_CLIENT_DATA
/* Define those to better describe your network interface. */
#define IFNAME0 'b'
#define IFNAME1 'r'
struct bridgeif_private_s;
typedef struct bridgeif_port_private_s {
struct bridgeif_private_s *bridge;
struct netif *port_netif;
u8_t port_num;
} bridgeif_port_t;
typedef struct bridgeif_fdb_static_entry_s {
u8_t used;
bridgeif_portmask_t dst_ports;
struct eth_addr addr;
} bridgeif_fdb_static_entry_t;
typedef struct bridgeif_private_s {
struct netif *netif;
struct eth_addr ethaddr;
u8_t max_ports;
u8_t num_ports;
bridgeif_port_t *ports;
u16_t max_fdbs_entries;
bridgeif_fdb_static_entry_t *fdbs;
u16_t max_fdbd_entries;
void *fdbd;
} bridgeif_private_t;
/* netif data index to get the bridge on input */
u8_t bridgeif_netif_client_id = 0xff;
/**
* @ingroup bridgeif
* Add a static entry to the forwarding database.
* A static entry marks where frames to a specific eth address (unicast or group address) are
* forwarded.
* bits [0..(BRIDGEIF_MAX_PORTS-1)]: hw ports
* bit [BRIDGEIF_MAX_PORTS]: cpu port
* 0: drop
*/
err_t
bridgeif_fdb_add(struct netif *bridgeif, const struct eth_addr *addr, bridgeif_portmask_t ports)
{
int i;
bridgeif_private_t *br;
BRIDGEIF_DECL_PROTECT(lev);
LWIP_ASSERT("invalid netif", bridgeif != NULL);
br = (bridgeif_private_t *)bridgeif->state;
LWIP_ASSERT("invalid state", br != NULL);
BRIDGEIF_READ_PROTECT(lev);
for (i = 0; i < br->max_fdbs_entries; i++) {
if (!br->fdbs[i].used) {
BRIDGEIF_WRITE_PROTECT(lev);
if (!br->fdbs[i].used) {
br->fdbs[i].used = 1;
br->fdbs[i].dst_ports = ports;
memcpy(&br->fdbs[i].addr, addr, sizeof(struct eth_addr));
BRIDGEIF_WRITE_UNPROTECT(lev);
BRIDGEIF_READ_UNPROTECT(lev);
return ERR_OK;
}
BRIDGEIF_WRITE_UNPROTECT(lev);
}
}
BRIDGEIF_READ_UNPROTECT(lev);
return ERR_MEM;
}
/**
* @ingroup bridgeif
* Remove a static entry from the forwarding database
*/
err_t
bridgeif_fdb_remove(struct netif *bridgeif, const struct eth_addr *addr)
{
int i;
bridgeif_private_t *br;
BRIDGEIF_DECL_PROTECT(lev);
LWIP_ASSERT("invalid netif", bridgeif != NULL);
br = (bridgeif_private_t *)bridgeif->state;
LWIP_ASSERT("invalid state", br != NULL);
BRIDGEIF_READ_PROTECT(lev);
for (i = 0; i < br->max_fdbs_entries; i++) {
if (br->fdbs[i].used && !memcmp(&br->fdbs[i].addr, addr, sizeof(struct eth_addr))) {
BRIDGEIF_WRITE_PROTECT(lev);
if (br->fdbs[i].used && !memcmp(&br->fdbs[i].addr, addr, sizeof(struct eth_addr))) {
memset(&br->fdbs[i], 0, sizeof(bridgeif_fdb_static_entry_t));
BRIDGEIF_WRITE_UNPROTECT(lev);
BRIDGEIF_READ_UNPROTECT(lev);
return ERR_OK;
}
BRIDGEIF_WRITE_UNPROTECT(lev);
}
}
BRIDGEIF_READ_UNPROTECT(lev);
return ERR_VAL;
}
/** Get the forwarding port(s) (as bit mask) for the specified destination mac address */
static bridgeif_portmask_t
bridgeif_find_dst_ports(bridgeif_private_t *br, struct eth_addr *dst_addr)
{
int i;
BRIDGEIF_DECL_PROTECT(lev);
BRIDGEIF_READ_PROTECT(lev);
/* first check for static entries */
for (i = 0; i < br->max_fdbs_entries; i++) {
if (br->fdbs[i].used) {
if (!memcmp(&br->fdbs[i].addr, dst_addr, sizeof(struct eth_addr))) {
bridgeif_portmask_t ret = br->fdbs[i].dst_ports;
BRIDGEIF_READ_UNPROTECT(lev);
return ret;
}
}
}
if (dst_addr->addr[0] & 1) {
/* no match found: flood remaining group address */
BRIDGEIF_READ_UNPROTECT(lev);
return BR_FLOOD;
}
BRIDGEIF_READ_UNPROTECT(lev);
/* no match found: check dynamic fdb for port or fall back to flooding */
return bridgeif_fdb_get_dst_ports(br->fdbd, dst_addr);
}
/** Helper function to see if a destination mac belongs to the bridge
* (bridge netif or one of the port netifs), in which case the frame
* is sent to the cpu only.
*/
static int
bridgeif_is_local_mac(bridgeif_private_t *br, struct eth_addr *addr)
{
int i;
BRIDGEIF_DECL_PROTECT(lev);
if (!memcmp(br->netif->hwaddr, addr, sizeof(struct eth_addr))) {
return 1;
}
BRIDGEIF_READ_PROTECT(lev);
for (i = 0; i < br->num_ports; i++) {
struct netif *portif = br->ports[i].port_netif;
if (portif != NULL) {
if (!memcmp(portif->hwaddr, addr, sizeof(struct eth_addr))) {
BRIDGEIF_READ_UNPROTECT(lev);
return 1;
}
}
}
BRIDGEIF_READ_UNPROTECT(lev);
return 0;
}
/* Output helper function */
static err_t
bridgeif_send_to_port(bridgeif_private_t *br, struct pbuf *p, u8_t dstport_idx)
{
if (dstport_idx < BRIDGEIF_MAX_PORTS) {
/* possibly an external port */
if (dstport_idx < br->max_ports) {
struct netif *portif = br->ports[dstport_idx].port_netif;
if ((portif != NULL) && (portif->linkoutput != NULL)) {
/* prevent sending out to rx port */
if (netif_get_index(portif) != p->if_idx) {
if (netif_is_link_up(portif)) {
LWIP_DEBUGF(BRIDGEIF_FW_DEBUG, ("br -> flood(%p:%d) -> %d\n", (void *)p, p->if_idx, netif_get_index(portif)));
return portif->linkoutput(portif, p);
}
}
}
}
} else {
LWIP_ASSERT("invalid port index", dstport_idx == BRIDGEIF_MAX_PORTS);
}
return ERR_OK;
}
/** Helper function to pass a pbuf to all ports marked in 'dstports'
*/
static err_t
bridgeif_send_to_ports(bridgeif_private_t *br, struct pbuf *p, bridgeif_portmask_t dstports)
{
err_t err, ret_err = ERR_OK;
u8_t i;
bridgeif_portmask_t mask = 1;
BRIDGEIF_DECL_PROTECT(lev);
BRIDGEIF_READ_PROTECT(lev);
for (i = 0; i < BRIDGEIF_MAX_PORTS; i++, mask = (bridgeif_portmask_t)(mask << 1)) {
if (dstports & mask) {
err = bridgeif_send_to_port(br, p, i);
if (err != ERR_OK) {
ret_err = err;
}
}
}
BRIDGEIF_READ_UNPROTECT(lev);
return ret_err;
}
/** Output function of the application port of the bridge (the one with an ip address).
* The forwarding port(s) where this pbuf is sent on is/are automatically selected
* from the FDB.
*/
static err_t
bridgeif_output(struct netif *netif, struct pbuf *p)
{
err_t err;
bridgeif_private_t *br = (bridgeif_private_t *)netif->state;
struct eth_addr *dst = (struct eth_addr *)(p->payload);
bridgeif_portmask_t dstports = bridgeif_find_dst_ports(br, dst);
err = bridgeif_send_to_ports(br, p, dstports);
MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p->tot_len);
if (((u8_t *)p->payload)[0] & 1) {
/* broadcast or multicast packet*/
MIB2_STATS_NETIF_INC(netif, ifoutnucastpkts);
} else {
/* unicast packet */
MIB2_STATS_NETIF_INC(netif, ifoutucastpkts);
}
/* increase ifoutdiscards or ifouterrors on error */
LINK_STATS_INC(link.xmit);
return err;
}
/** The actual bridge input function. Port netif's input is changed to call
* here. This function decides where the frame is forwarded.
*/
static err_t
bridgeif_input(struct pbuf *p, struct netif *netif)
{
u8_t rx_idx;
bridgeif_portmask_t dstports;
struct eth_addr *src, *dst;
bridgeif_private_t *br;
bridgeif_port_t *port;
if (p == NULL || netif == NULL) {
return ERR_VAL;
}
port = (bridgeif_port_t *)netif_get_client_data(netif, bridgeif_netif_client_id);
LWIP_ASSERT("port data not set", port != NULL);
if (port == NULL || port->bridge == NULL) {
return ERR_VAL;
}
br = (bridgeif_private_t *)port->bridge;
rx_idx = netif_get_index(netif);
/* store receive index in pbuf */
p->if_idx = rx_idx;
dst = (struct eth_addr *)p->payload;
src = (struct eth_addr *)(((u8_t *)p->payload) + sizeof(struct eth_addr));
if ((src->addr[0] & 1) == 0) {
/* update src for all non-group addresses */
bridgeif_fdb_update_src(br->fdbd, src, port->port_num);
}
if (dst->addr[0] & 1) {
/* group address -> flood + cpu? */
dstports = bridgeif_find_dst_ports(br, dst);
bridgeif_send_to_ports(br, p, dstports);
if (dstports & (1 << BRIDGEIF_MAX_PORTS)) {
/* we pass the reference to ->input or have to free it */
LWIP_DEBUGF(BRIDGEIF_FW_DEBUG, ("br -> input(%p)\n", (void *)p));
if (br->netif->input(p, br->netif) != ERR_OK) {
pbuf_free(p);
}
} else {
/* all references done */
pbuf_free(p);
}
/* always return ERR_OK here to prevent the caller freeing the pbuf */
return ERR_OK;
} else {
/* is this for one of the local ports? */
if (bridgeif_is_local_mac(br, dst)) {
/* yes, send to cpu port only */
LWIP_DEBUGF(BRIDGEIF_FW_DEBUG, ("br -> input(%p)\n", (void *)p));
return br->netif->input(p, br->netif);
}
/* get dst port */
dstports = bridgeif_find_dst_ports(br, dst);
bridgeif_send_to_ports(br, p, dstports);
/* no need to send to cpu, flooding is for external ports only */
/* by this, we consumed the pbuf */
pbuf_free(p);
/* always return ERR_OK here to prevent the caller freeing the pbuf */
return ERR_OK;
}
}
#if !BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT
/** Input function for port netifs used to synchronize into tcpip_thread.
*/
static err_t
bridgeif_tcpip_input(struct pbuf *p, struct netif *netif)
{
return tcpip_inpkt(p, netif, bridgeif_input);
}
#endif /* BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT */
/**
* @ingroup bridgeif
* Initialization function passed to netif_add().
*
* ATTENTION: A pointer to a @ref bridgeif_initdata_t must be passed as 'state'
* to @ref netif_add when adding the bridge. I supplies MAC address
* and controls memory allocation (number of ports, FDB size).
*
* @param netif the lwip network interface structure for this ethernetif
* @return ERR_OK if the loopif is initialized
* ERR_MEM if private data couldn't be allocated
* any other err_t on error
*/
err_t
bridgeif_init(struct netif *netif)
{
bridgeif_initdata_t *init_data;
bridgeif_private_t *br;
size_t alloc_len_sizet;
mem_size_t alloc_len;
LWIP_ASSERT("netif != NULL", (netif != NULL));
LWIP_ASSERT("bridgeif needs an input callback", (netif->input != NULL));
#if !BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT
if (netif->input == tcpip_input) {
LWIP_DEBUGF(BRIDGEIF_DEBUG | LWIP_DBG_ON, ("bridgeif does not need tcpip_input, use netif_input/ethernet_input instead"));
}
#endif
if (bridgeif_netif_client_id == 0xFF) {
bridgeif_netif_client_id = netif_alloc_client_data_id();
}
init_data = (bridgeif_initdata_t *)netif->state;
LWIP_ASSERT("init_data != NULL", (init_data != NULL));
LWIP_ASSERT("init_data->max_ports <= BRIDGEIF_MAX_PORTS",
init_data->max_ports <= BRIDGEIF_MAX_PORTS);
alloc_len_sizet = sizeof(bridgeif_private_t) + (init_data->max_ports * sizeof(bridgeif_port_t) + (init_data->max_fdb_static_entries * sizeof(bridgeif_fdb_static_entry_t)));
alloc_len = (mem_size_t)alloc_len_sizet;
LWIP_ASSERT("alloc_len == alloc_len_sizet", alloc_len == alloc_len_sizet);
LWIP_DEBUGF(BRIDGEIF_DEBUG, ("bridgeif_init: allocating %d bytes for private data\n", (int)alloc_len));
br = (bridgeif_private_t *)mem_calloc(1, alloc_len);
if (br == NULL) {
LWIP_DEBUGF(NETIF_DEBUG, ("bridgeif_init: out of memory\n"));
return ERR_MEM;
}
memcpy(&br->ethaddr, &init_data->ethaddr, sizeof(br->ethaddr));
br->netif = netif;
br->max_ports = init_data->max_ports;
br->ports = (bridgeif_port_t *)(br + 1);
br->max_fdbs_entries = init_data->max_fdb_static_entries;
br->fdbs = (bridgeif_fdb_static_entry_t *)(((u8_t *)(br + 1)) + (init_data->max_ports * sizeof(bridgeif_port_t)));
br->max_fdbd_entries = init_data->max_fdb_dynamic_entries;
br->fdbd = bridgeif_fdb_init(init_data->max_fdb_dynamic_entries);
if (br->fdbd == NULL) {
LWIP_DEBUGF(NETIF_DEBUG, ("bridgeif_init: out of memory in fdb_init\n"));
mem_free(br);
return ERR_MEM;
}
#if LWIP_NETIF_HOSTNAME
/* Initialize interface hostname */
netif->hostname = "lwip";
#endif /* LWIP_NETIF_HOSTNAME */
/*
* Initialize the snmp variables and counters inside the struct netif.
* The last argument should be replaced with your link speed, in units
* of bits per second.
*/
MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, 0);
netif->state = br;
netif->name[0] = IFNAME0;
netif->name[1] = IFNAME1;
/* We directly use etharp_output() here to save a function call.
* You can instead declare your own function an call etharp_output()
* from it if you have to do some checks before sending (e.g. if link
* is available...) */
#if LWIP_IPV4
netif->output = etharp_output;
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
netif->output_ip6 = ethip6_output;
#endif /* LWIP_IPV6 */
netif->linkoutput = bridgeif_output;
/* set MAC hardware address length */
netif->hwaddr_len = ETH_HWADDR_LEN;
/* set MAC hardware address */
memcpy(netif->hwaddr, &br->ethaddr, ETH_HWADDR_LEN);
/* maximum transfer unit */
netif->mtu = 1500;
/* device capabilities */
/* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET | NETIF_FLAG_IGMP | NETIF_FLAG_MLD6 | NETIF_FLAG_LINK_UP;
#if LWIP_IPV6 && LWIP_IPV6_MLD
/*
* For hardware/netifs that implement MAC filtering.
* All-nodes link-local is handled by default, so we must let the hardware know
* to allow multicast packets in.
* Should set mld_mac_filter previously. */
if (netif->mld_mac_filter != NULL) {
ip6_addr_t ip6_allnodes_ll;
ip6_addr_set_allnodes_linklocal(&ip6_allnodes_ll);
netif->mld_mac_filter(netif, &ip6_allnodes_ll, NETIF_ADD_MAC_FILTER);
}
#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
return ERR_OK;
}
/**
* @ingroup bridgeif
* Add a port to the bridge
*/
err_t
bridgeif_add_port(struct netif *bridgeif, struct netif *portif)
{
bridgeif_private_t *br;
bridgeif_port_t *port;
LWIP_ASSERT("bridgeif != NULL", bridgeif != NULL);
LWIP_ASSERT("bridgeif->state != NULL", bridgeif->state != NULL);
LWIP_ASSERT("portif != NULL", portif != NULL);
if (!(portif->flags & NETIF_FLAG_ETHARP) || !(portif->flags & NETIF_FLAG_ETHERNET)) {
/* can only add ETHERNET/ETHARP interfaces */
return ERR_VAL;
}
br = (bridgeif_private_t *)bridgeif->state;
if (br->num_ports >= br->max_ports) {
return ERR_VAL;
}
port = &br->ports[br->num_ports];
port->port_netif = portif;
port->port_num = br->num_ports;
port->bridge = br;
br->num_ports++;
/* let the port call us on input */
#if BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT
portif->input = bridgeif_input;
#else
portif->input = bridgeif_tcpip_input;
#endif
/* store pointer to bridge in netif */
netif_set_client_data(portif, bridgeif_netif_client_id, port);
/* remove ETHARP flag to prevent sending report events on netif-up */
netif_clear_flags(portif, NETIF_FLAG_ETHARP);
return ERR_OK;
}
#endif /* LWIP_NUM_NETIF_CLIENT_DATA */

View File

@@ -0,0 +1,212 @@
/**
* @file
* lwIP netif implementing an FDB for IEEE 802.1D MAC Bridge
*/
/*
* Copyright (c) 2017 Simon Goldschmidt.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Simon Goldschmidt <goldsimon@gmx.de>
*
*/
/**
* @defgroup bridgeif_fdb FDB example code
* @ingroup bridgeif
* This file implements an example for an FDB (Forwarding DataBase)
*/
#include "netif/bridgeif.h"
#include "lwip/sys.h"
#include "lwip/mem.h"
#include "lwip/timeouts.h"
#include <string.h>
#define BRIDGEIF_AGE_TIMER_MS 1000
#define BR_FDB_TIMEOUT_SEC (60*5) /* 5 minutes FDB timeout */
typedef struct bridgeif_dfdb_entry_s {
u8_t used;
u8_t port;
u32_t ts;
struct eth_addr addr;
} bridgeif_dfdb_entry_t;
typedef struct bridgeif_dfdb_s {
u16_t max_fdb_entries;
bridgeif_dfdb_entry_t *fdb;
} bridgeif_dfdb_t;
/**
* @ingroup bridgeif_fdb
* A real simple and slow implementation of an auto-learning forwarding database that
* remembers known src mac addresses to know which port to send frames destined for that
* mac address.
*
* ATTENTION: This is meant as an example only, in real-world use, you should
* provide a better implementation :-)
*/
void
bridgeif_fdb_update_src(void *fdb_ptr, struct eth_addr *src_addr, u8_t port_idx)
{
int i;
bridgeif_dfdb_t *fdb = (bridgeif_dfdb_t *)fdb_ptr;
BRIDGEIF_DECL_PROTECT(lev);
BRIDGEIF_READ_PROTECT(lev);
for (i = 0; i < fdb->max_fdb_entries; i++) {
bridgeif_dfdb_entry_t *e = &fdb->fdb[i];
if (e->used && e->ts) {
if (!memcmp(&e->addr, src_addr, sizeof(struct eth_addr))) {
LWIP_DEBUGF(BRIDGEIF_FDB_DEBUG, ("br: update src %02x:%02x:%02x:%02x:%02x:%02x (from %d) @ idx %d\n",
src_addr->addr[0], src_addr->addr[1], src_addr->addr[2], src_addr->addr[3], src_addr->addr[4], src_addr->addr[5],
port_idx, i));
BRIDGEIF_WRITE_PROTECT(lev);
e->ts = BR_FDB_TIMEOUT_SEC;
e->port = port_idx;
BRIDGEIF_WRITE_UNPROTECT(lev);
BRIDGEIF_READ_UNPROTECT(lev);
return;
}
}
}
/* not found, allocate new entry from free */
for (i = 0; i < fdb->max_fdb_entries; i++) {
bridgeif_dfdb_entry_t *e = &fdb->fdb[i];
if (!e->used || !e->ts) {
BRIDGEIF_WRITE_PROTECT(lev);
/* check again when protected */
if (!e->used || !e->ts) {
LWIP_DEBUGF(BRIDGEIF_FDB_DEBUG, ("br: create src %02x:%02x:%02x:%02x:%02x:%02x (from %d) @ idx %d\n",
src_addr->addr[0], src_addr->addr[1], src_addr->addr[2], src_addr->addr[3], src_addr->addr[4], src_addr->addr[5],
port_idx, i));
memcpy(&e->addr, src_addr, sizeof(struct eth_addr));
e->ts = BR_FDB_TIMEOUT_SEC;
e->port = port_idx;
e->used = 1;
BRIDGEIF_WRITE_UNPROTECT(lev);
BRIDGEIF_READ_UNPROTECT(lev);
return;
}
BRIDGEIF_WRITE_UNPROTECT(lev);
}
}
BRIDGEIF_READ_UNPROTECT(lev);
/* not found, no free entry -> flood */
}
/**
* @ingroup bridgeif_fdb
* Walk our list of auto-learnt fdb entries and return a port to forward or BR_FLOOD if unknown
*/
bridgeif_portmask_t
bridgeif_fdb_get_dst_ports(void *fdb_ptr, struct eth_addr *dst_addr)
{
int i;
bridgeif_dfdb_t *fdb = (bridgeif_dfdb_t *)fdb_ptr;
BRIDGEIF_DECL_PROTECT(lev);
BRIDGEIF_READ_PROTECT(lev);
for (i = 0; i < fdb->max_fdb_entries; i++) {
bridgeif_dfdb_entry_t *e = &fdb->fdb[i];
if (e->used && e->ts) {
if (!memcmp(&e->addr, dst_addr, sizeof(struct eth_addr))) {
bridgeif_portmask_t ret = (bridgeif_portmask_t)(1 << e->port);
BRIDGEIF_READ_UNPROTECT(lev);
return ret;
}
}
}
BRIDGEIF_READ_UNPROTECT(lev);
return BR_FLOOD;
}
/**
* @ingroup bridgeif_fdb
* Aging implementation of our simple fdb
*/
static void
bridgeif_fdb_age_one_second(void *fdb_ptr)
{
int i;
bridgeif_dfdb_t *fdb;
BRIDGEIF_DECL_PROTECT(lev);
fdb = (bridgeif_dfdb_t *)fdb_ptr;
BRIDGEIF_READ_PROTECT(lev);
for (i = 0; i < fdb->max_fdb_entries; i++) {
bridgeif_dfdb_entry_t *e = &fdb->fdb[i];
if (e->used && e->ts) {
BRIDGEIF_WRITE_PROTECT(lev);
/* check again when protected */
if (e->used && e->ts) {
if (--e->ts == 0) {
e->used = 0;
}
}
BRIDGEIF_WRITE_UNPROTECT(lev);
}
}
BRIDGEIF_READ_UNPROTECT(lev);
}
/** Timer callback for fdb aging, called once per second */
static void
bridgeif_age_tmr(void *arg)
{
bridgeif_dfdb_t *fdb = (bridgeif_dfdb_t *)arg;
LWIP_ASSERT("invalid arg", arg != NULL);
bridgeif_fdb_age_one_second(fdb);
sys_timeout(BRIDGEIF_AGE_TIMER_MS, bridgeif_age_tmr, arg);
}
/**
* @ingroup bridgeif_fdb
* Init our simple fdb list
*/
void *
bridgeif_fdb_init(u16_t max_fdb_entries)
{
bridgeif_dfdb_t *fdb;
size_t alloc_len_sizet = sizeof(bridgeif_dfdb_t) + (max_fdb_entries * sizeof(bridgeif_dfdb_entry_t));
mem_size_t alloc_len = (mem_size_t)alloc_len_sizet;
LWIP_ASSERT("alloc_len == alloc_len_sizet", alloc_len == alloc_len_sizet);
LWIP_DEBUGF(BRIDGEIF_DEBUG, ("bridgeif_fdb_init: allocating %d bytes for private FDB data\n", (int)alloc_len));
fdb = (bridgeif_dfdb_t *)mem_calloc(1, alloc_len);
if (fdb == NULL) {
return NULL;
}
fdb->max_fdb_entries = max_fdb_entries;
fdb->fdb = (bridgeif_dfdb_entry_t *)(fdb + 1);
sys_timeout(BRIDGEIF_AGE_TIMER_MS, bridgeif_age_tmr, fdb);
return fdb;
}

View File

@@ -0,0 +1,321 @@
/**
* @file
* Ethernet common functions
*
* @defgroup ethernet Ethernet
* @ingroup callbackstyle_api
*/
/*
* Copyright (c) 2001-2003 Swedish Institute of Computer Science.
* Copyright (c) 2003-2004 Leon Woestenberg <leon.woestenberg@axon.tv>
* Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
*
* This file is part of the lwIP TCP/IP stack.
*
*/
#include "lwip/opt.h"
#if LWIP_ARP || LWIP_ETHERNET
#include "netif/ethernet.h"
#include "lwip/def.h"
#include "lwip/stats.h"
#include "lwip/etharp.h"
#include "lwip/ip.h"
#include "lwip/snmp.h"
#include <string.h>
#include "netif/ppp/ppp_opts.h"
#if PPPOE_SUPPORT
#include "netif/ppp/pppoe.h"
#endif /* PPPOE_SUPPORT */
#ifdef LWIP_HOOK_FILENAME
#include LWIP_HOOK_FILENAME
#endif
const struct eth_addr ethbroadcast = {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}};
const struct eth_addr ethzero = {{0, 0, 0, 0, 0, 0}};
/**
* @ingroup lwip_nosys
* Process received ethernet frames. Using this function instead of directly
* calling ip_input and passing ARP frames through etharp in ethernetif_input,
* the ARP cache is protected from concurrent access.\n
* Don't call directly, pass to netif_add() and call netif->input().
*
* @param p the received packet, p->payload pointing to the ethernet header
* @param netif the network interface on which the packet was received
*
* @see LWIP_HOOK_UNKNOWN_ETH_PROTOCOL
* @see ETHARP_SUPPORT_VLAN
* @see LWIP_HOOK_VLAN_CHECK
*/
err_t
ethernet_input(struct pbuf *p, struct netif *netif)
{
struct eth_hdr *ethhdr;
u16_t type;
#if LWIP_ARP || ETHARP_SUPPORT_VLAN || LWIP_IPV6
u16_t next_hdr_offset = SIZEOF_ETH_HDR;
#endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */
LWIP_ASSERT_CORE_LOCKED();
if (p->len <= SIZEOF_ETH_HDR) {
/* a packet with only an ethernet header (or less) is not valid for us */
ETHARP_STATS_INC(etharp.proterr);
ETHARP_STATS_INC(etharp.drop);
MIB2_STATS_NETIF_INC(netif, ifinerrors);
goto free_and_return;
}
if (p->if_idx == NETIF_NO_INDEX) {
p->if_idx = netif_get_index(netif);
}
/* points to packet payload, which starts with an Ethernet header */
ethhdr = (struct eth_hdr *)p->payload;
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n",
(unsigned char)ethhdr->dest.addr[0], (unsigned char)ethhdr->dest.addr[1], (unsigned char)ethhdr->dest.addr[2],
(unsigned char)ethhdr->dest.addr[3], (unsigned char)ethhdr->dest.addr[4], (unsigned char)ethhdr->dest.addr[5],
(unsigned char)ethhdr->src.addr[0], (unsigned char)ethhdr->src.addr[1], (unsigned char)ethhdr->src.addr[2],
(unsigned char)ethhdr->src.addr[3], (unsigned char)ethhdr->src.addr[4], (unsigned char)ethhdr->src.addr[5],
lwip_htons(ethhdr->type)));
type = ethhdr->type;
#if ETHARP_SUPPORT_VLAN
if (type == PP_HTONS(ETHTYPE_VLAN)) {
struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr *)(((char *)ethhdr) + SIZEOF_ETH_HDR);
next_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR;
if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) {
/* a packet with only an ethernet/vlan header (or less) is not valid for us */
ETHARP_STATS_INC(etharp.proterr);
ETHARP_STATS_INC(etharp.drop);
MIB2_STATS_NETIF_INC(netif, ifinerrors);
goto free_and_return;
}
#if defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) /* if not, allow all VLANs */
#ifdef LWIP_HOOK_VLAN_CHECK
if (!LWIP_HOOK_VLAN_CHECK(netif, ethhdr, vlan)) {
#elif defined(ETHARP_VLAN_CHECK_FN)
if (!ETHARP_VLAN_CHECK_FN(ethhdr, vlan)) {
#elif defined(ETHARP_VLAN_CHECK)
if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) {
#endif
/* silently ignore this packet: not for our VLAN */
pbuf_free(p);
return ERR_OK;
}
#endif /* defined(LWIP_HOOK_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */
type = vlan->tpid;
}
#endif /* ETHARP_SUPPORT_VLAN */
#if LWIP_ARP_FILTER_NETIF
netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, lwip_htons(type));
#endif /* LWIP_ARP_FILTER_NETIF*/
if (ethhdr->dest.addr[0] & 1) {
/* this might be a multicast or broadcast packet */
if (ethhdr->dest.addr[0] == LL_IP4_MULTICAST_ADDR_0) {
#if LWIP_IPV4
if ((ethhdr->dest.addr[1] == LL_IP4_MULTICAST_ADDR_1) &&
(ethhdr->dest.addr[2] == LL_IP4_MULTICAST_ADDR_2)) {
/* mark the pbuf as link-layer multicast */
p->flags |= PBUF_FLAG_LLMCAST;
}
#endif /* LWIP_IPV4 */
}
#if LWIP_IPV6
else if ((ethhdr->dest.addr[0] == LL_IP6_MULTICAST_ADDR_0) &&
(ethhdr->dest.addr[1] == LL_IP6_MULTICAST_ADDR_1)) {
/* mark the pbuf as link-layer multicast */
p->flags |= PBUF_FLAG_LLMCAST;
}
#endif /* LWIP_IPV6 */
else if (eth_addr_cmp(&ethhdr->dest, &ethbroadcast)) {
/* mark the pbuf as link-layer broadcast */
p->flags |= PBUF_FLAG_LLBCAST;
}
}
switch (type) {
#if LWIP_IPV4 && LWIP_ARP
/* IP packet? */
case PP_HTONS(ETHTYPE_IP):
if (!(netif->flags & NETIF_FLAG_ETHARP)) {
goto free_and_return;
}
/* skip Ethernet header (min. size checked above) */
if (pbuf_remove_header(p, next_hdr_offset)) {
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
("ethernet_input: IPv4 packet dropped, too short (%"U16_F"/%"U16_F")\n",
p->tot_len, next_hdr_offset));
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet"));
goto free_and_return;
} else {
/* pass to IP layer */
ip4_input(p, netif);
}
break;
case PP_HTONS(ETHTYPE_ARP):
if (!(netif->flags & NETIF_FLAG_ETHARP)) {
goto free_and_return;
}
/* skip Ethernet header (min. size checked above) */
if (pbuf_remove_header(p, next_hdr_offset)) {
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
("ethernet_input: ARP response packet dropped, too short (%"U16_F"/%"U16_F")\n",
p->tot_len, next_hdr_offset));
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("Can't move over header in packet"));
ETHARP_STATS_INC(etharp.lenerr);
ETHARP_STATS_INC(etharp.drop);
goto free_and_return;
} else {
/* pass p to ARP module */
etharp_input(p, netif);
}
break;
#endif /* LWIP_IPV4 && LWIP_ARP */
#if PPPOE_SUPPORT
case PP_HTONS(ETHTYPE_PPPOEDISC): /* PPP Over Ethernet Discovery Stage */
pppoe_disc_input(netif, p);
break;
case PP_HTONS(ETHTYPE_PPPOE): /* PPP Over Ethernet Session Stage */
pppoe_data_input(netif, p);
break;
#endif /* PPPOE_SUPPORT */
#if LWIP_IPV6
case PP_HTONS(ETHTYPE_IPV6): /* IPv6 */
/* skip Ethernet header */
if ((p->len < next_hdr_offset) || pbuf_remove_header(p, next_hdr_offset)) {
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
("ethernet_input: IPv6 packet dropped, too short (%"U16_F"/%"U16_F")\n",
p->tot_len, next_hdr_offset));
goto free_and_return;
} else {
/* pass to IPv6 layer */
ip6_input(p, netif);
}
break;
#endif /* LWIP_IPV6 */
default:
#ifdef LWIP_HOOK_UNKNOWN_ETH_PROTOCOL
if (LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(p, netif) == ERR_OK) {
break;
}
#endif
ETHARP_STATS_INC(etharp.proterr);
ETHARP_STATS_INC(etharp.drop);
MIB2_STATS_NETIF_INC(netif, ifinunknownprotos);
goto free_and_return;
}
/* This means the pbuf is freed or consumed,
so the caller doesn't have to free it again */
return ERR_OK;
free_and_return:
pbuf_free(p);
return ERR_OK;
}
/**
* @ingroup ethernet
* Send an ethernet packet on the network using netif->linkoutput().
* The ethernet header is filled in before sending.
*
* @see LWIP_HOOK_VLAN_SET
*
* @param netif the lwIP network interface on which to send the packet
* @param p the packet to send. pbuf layer must be @ref PBUF_LINK.
* @param src the source MAC address to be copied into the ethernet header
* @param dst the destination MAC address to be copied into the ethernet header
* @param eth_type ethernet type (@ref lwip_ieee_eth_type)
* @return ERR_OK if the packet was sent, any other err_t on failure
*/
err_t
ethernet_output(struct netif * netif, struct pbuf * p,
const struct eth_addr * src, const struct eth_addr * dst,
u16_t eth_type) {
struct eth_hdr *ethhdr;
u16_t eth_type_be = lwip_htons(eth_type);
#if ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET)
s32_t vlan_prio_vid = LWIP_HOOK_VLAN_SET(netif, p, src, dst, eth_type);
if (vlan_prio_vid >= 0) {
struct eth_vlan_hdr *vlanhdr;
LWIP_ASSERT("prio_vid must be <= 0xFFFF", vlan_prio_vid <= 0xFFFF);
if (pbuf_add_header(p, SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) != 0) {
goto pbuf_header_failed;
}
vlanhdr = (struct eth_vlan_hdr *)(((u8_t *)p->payload) + SIZEOF_ETH_HDR);
vlanhdr->tpid = eth_type_be;
vlanhdr->prio_vid = lwip_htons((u16_t)vlan_prio_vid);
eth_type_be = PP_HTONS(ETHTYPE_VLAN);
} else
#endif /* ETHARP_SUPPORT_VLAN && defined(LWIP_HOOK_VLAN_SET) */
{
if (pbuf_add_header(p, SIZEOF_ETH_HDR) != 0) {
goto pbuf_header_failed;
}
}
LWIP_ASSERT_CORE_LOCKED();
ethhdr = (struct eth_hdr *)p->payload;
ethhdr->type = eth_type_be;
SMEMCPY(&ethhdr->dest, dst, ETH_HWADDR_LEN);
SMEMCPY(&ethhdr->src, src, ETH_HWADDR_LEN);
LWIP_ASSERT("netif->hwaddr_len must be 6 for ethernet_output!",
(netif->hwaddr_len == ETH_HWADDR_LEN));
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
("ethernet_output: sending packet %p\n", (void *)p));
/* send the packet */
return netif->linkoutput(netif, p);
pbuf_header_failed:
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
("ethernet_output: could not allocate room for header.\n"));
LINK_STATS_INC(link.lenerr);
return ERR_BUF;
}
#endif /* LWIP_ARP || LWIP_ETHERNET */

View File

@@ -0,0 +1,920 @@
/**
* @file
*
* 6LowPAN output for IPv6. Uses ND tables for link-layer addressing. Fragments packets to 6LowPAN units.
*
* This implementation aims to conform to IEEE 802.15.4(-2015), RFC 4944 and RFC 6282.
* @todo: RFC 6775.
*/
/*
* Copyright (c) 2015 Inico Technologies Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Ivan Delamer <delamer@inicotech.com>
*
*
* Please coordinate changes and requests with Ivan Delamer
* <delamer@inicotech.com>
*/
/**
* @defgroup sixlowpan 6LoWPAN (RFC4944)
* @ingroup netifs
* 6LowPAN netif implementation
*/
#include "netif/lowpan6.h"
#if LWIP_IPV6
#include "lwip/ip.h"
#include "lwip/pbuf.h"
#include "lwip/ip_addr.h"
#include "lwip/netif.h"
#include "lwip/nd6.h"
#include "lwip/mem.h"
#include "lwip/udp.h"
#include "lwip/tcpip.h"
#include "lwip/snmp.h"
#include "netif/ieee802154.h"
#include <string.h>
#if LWIP_6LOWPAN_802154_HW_CRC
#define LWIP_6LOWPAN_DO_CALC_CRC(buf, len) 0
#else
#define LWIP_6LOWPAN_DO_CALC_CRC(buf, len) LWIP_6LOWPAN_CALC_CRC(buf, len)
#endif
/** This is a helper struct for reassembly of fragments
* (IEEE 802.15.4 limits to 127 bytes)
*/
struct lowpan6_reass_helper {
struct lowpan6_reass_helper *next_packet;
struct pbuf *reass;
struct pbuf *frags;
u8_t timer;
struct lowpan6_link_addr sender_addr;
u16_t datagram_size;
u16_t datagram_tag;
};
/** This struct keeps track of per-netif state */
struct lowpan6_ieee802154_data {
/** fragment reassembly list */
struct lowpan6_reass_helper *reass_list;
#if LWIP_6LOWPAN_NUM_CONTEXTS > 0
/** address context for compression */
ip6_addr_t lowpan6_context[LWIP_6LOWPAN_NUM_CONTEXTS];
#endif
/** Datagram Tag for fragmentation */
u16_t tx_datagram_tag;
/** local PAN ID for IEEE 802.15.4 header */
u16_t ieee_802154_pan_id;
/** Sequence Number for IEEE 802.15.4 transmission */
u8_t tx_frame_seq_num;
};
/* Maximum frame size is 127 bytes minus CRC size */
#define LOWPAN6_MAX_PAYLOAD (127 - 2)
/** Currently, this state is global, since there's only one 6LoWPAN netif */
static struct lowpan6_ieee802154_data lowpan6_data;
#if LWIP_6LOWPAN_NUM_CONTEXTS > 0
#define LWIP_6LOWPAN_CONTEXTS(netif) lowpan6_data.lowpan6_context
#else
#define LWIP_6LOWPAN_CONTEXTS(netif) NULL
#endif
static const struct lowpan6_link_addr ieee_802154_broadcast = {2, {0xff, 0xff}};
#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS
static struct lowpan6_link_addr short_mac_addr = {2, {0, 0}};
#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */
/* IEEE 802.15.4 specific functions: */
/** Write the IEEE 802.15.4 header that encapsulates the 6LoWPAN frame.
* Src and dst PAN IDs are filled with the ID set by @ref lowpan6_set_pan_id.
*
* Since the length is variable:
* @returns the header length
*/
static u8_t
lowpan6_write_iee802154_header(struct ieee_802154_hdr *hdr, const struct lowpan6_link_addr *src,
const struct lowpan6_link_addr *dst)
{
u8_t ieee_header_len;
u8_t *buffer;
u8_t i;
u16_t fc;
fc = IEEE_802154_FC_FT_DATA; /* send data packet (2003 frame version) */
fc |= IEEE_802154_FC_PANID_COMPR; /* set PAN ID compression, for now src and dst PANs are equal */
if (dst != &ieee_802154_broadcast) {
fc |= IEEE_802154_FC_ACK_REQ; /* data packet, no broadcast: ack required. */
}
if (dst->addr_len == 2) {
fc |= IEEE_802154_FC_DST_ADDR_MODE_SHORT;
} else {
LWIP_ASSERT("invalid dst address length", dst->addr_len == 8);
fc |= IEEE_802154_FC_DST_ADDR_MODE_EXT;
}
if (src->addr_len == 2) {
fc |= IEEE_802154_FC_SRC_ADDR_MODE_SHORT;
} else {
LWIP_ASSERT("invalid src address length", src->addr_len == 8);
fc |= IEEE_802154_FC_SRC_ADDR_MODE_EXT;
}
hdr->frame_control = fc;
hdr->sequence_number = lowpan6_data.tx_frame_seq_num++;
hdr->destination_pan_id = lowpan6_data.ieee_802154_pan_id; /* pan id */
buffer = (u8_t *)hdr;
ieee_header_len = 5;
i = dst->addr_len;
/* reverse memcpy of dst addr */
while (i-- > 0) {
buffer[ieee_header_len++] = dst->addr[i];
}
/* Source PAN ID skipped due to PAN ID Compression */
i = src->addr_len;
/* reverse memcpy of src addr */
while (i-- > 0) {
buffer[ieee_header_len++] = src->addr[i];
}
return ieee_header_len;
}
/** Parse the IEEE 802.15.4 header from a pbuf.
* If successful, the header is hidden from the pbuf.
*
* PAN IDs and seuqence number are not checked
*
* @param p input pbuf, p->payload pointing at the IEEE 802.15.4 header
* @param src pointer to source address filled from the header
* @param dest pointer to destination address filled from the header
* @returns ERR_OK if successful
*/
static err_t
lowpan6_parse_iee802154_header(struct pbuf *p, struct lowpan6_link_addr *src,
struct lowpan6_link_addr *dest)
{
u8_t *puc;
s8_t i;
u16_t frame_control, addr_mode;
u16_t datagram_offset;
/* Parse IEEE 802.15.4 header */
puc = (u8_t *)p->payload;
frame_control = puc[0] | (puc[1] << 8);
datagram_offset = 2;
if (frame_control & IEEE_802154_FC_SEQNO_SUPPR) {
if (IEEE_802154_FC_FRAME_VERSION_GET(frame_control) <= 1) {
/* sequence number suppressed, this is not valid for versions 0/1 */
return ERR_VAL;
}
} else {
datagram_offset++;
}
datagram_offset += 2; /* Skip destination PAN ID */
addr_mode = frame_control & IEEE_802154_FC_DST_ADDR_MODE_MASK;
if (addr_mode == IEEE_802154_FC_DST_ADDR_MODE_EXT) {
/* extended address (64 bit) */
dest->addr_len = 8;
/* reverse memcpy: */
for (i = 0; i < 8; i++) {
dest->addr[i] = puc[datagram_offset + 7 - i];
}
datagram_offset += 8;
} else if (addr_mode == IEEE_802154_FC_DST_ADDR_MODE_SHORT) {
/* short address (16 bit) */
dest->addr_len = 2;
/* reverse memcpy: */
dest->addr[0] = puc[datagram_offset + 1];
dest->addr[1] = puc[datagram_offset];
datagram_offset += 2;
} else {
/* unsupported address mode (do we need "no address"?) */
return ERR_VAL;
}
if (!(frame_control & IEEE_802154_FC_PANID_COMPR)) {
/* No PAN ID compression, skip source PAN ID */
datagram_offset += 2;
}
addr_mode = frame_control & IEEE_802154_FC_SRC_ADDR_MODE_MASK;
if (addr_mode == IEEE_802154_FC_SRC_ADDR_MODE_EXT) {
/* extended address (64 bit) */
src->addr_len = 8;
/* reverse memcpy: */
for (i = 0; i < 8; i++) {
src->addr[i] = puc[datagram_offset + 7 - i];
}
datagram_offset += 8;
} else if (addr_mode == IEEE_802154_FC_DST_ADDR_MODE_SHORT) {
/* short address (16 bit) */
src->addr_len = 2;
src->addr[0] = puc[datagram_offset + 1];
src->addr[1] = puc[datagram_offset];
datagram_offset += 2;
} else {
/* unsupported address mode (do we need "no address"?) */
return ERR_VAL;
}
/* hide IEEE802.15.4 header. */
if (pbuf_remove_header(p, datagram_offset)) {
return ERR_VAL;
}
return ERR_OK;
}
/** Calculate the 16-bit CRC as required by IEEE 802.15.4 */
u16_t
lowpan6_calc_crc(const void* buf, u16_t len)
{
#define CCITT_POLY_16 0x8408U
u16_t i;
u8_t b;
u16_t crc = 0;
const u8_t* p = (const u8_t*)buf;
for (i = 0; i < len; i++) {
u8_t data = *p;
for (b = 0U; b < 8U; b++) {
if (((data ^ crc) & 1) != 0) {
crc = (u16_t)((crc >> 1) ^ CCITT_POLY_16);
} else {
crc = (u16_t)(crc >> 1);
}
data = (u8_t)(data >> 1);
}
p++;
}
return crc;
}
/* Fragmentation specific functions: */
static void
free_reass_datagram(struct lowpan6_reass_helper *lrh)
{
if (lrh->reass) {
pbuf_free(lrh->reass);
}
if (lrh->frags) {
pbuf_free(lrh->frags);
}
mem_free(lrh);
}
/**
* Removes a datagram from the reassembly queue.
**/
static void
dequeue_datagram(struct lowpan6_reass_helper *lrh, struct lowpan6_reass_helper *prev)
{
if (lowpan6_data.reass_list == lrh) {
lowpan6_data.reass_list = lowpan6_data.reass_list->next_packet;
} else {
/* it wasn't the first, so it must have a valid 'prev' */
LWIP_ASSERT("sanity check linked list", prev != NULL);
prev->next_packet = lrh->next_packet;
}
}
/**
* Periodic timer for 6LowPAN functions:
*
* - Remove incomplete/old packets
*/
void
lowpan6_tmr(void)
{
struct lowpan6_reass_helper *lrh, *lrh_next, *lrh_prev = NULL;
lrh = lowpan6_data.reass_list;
while (lrh != NULL) {
lrh_next = lrh->next_packet;
if ((--lrh->timer) == 0) {
dequeue_datagram(lrh, lrh_prev);
free_reass_datagram(lrh);
} else {
lrh_prev = lrh;
}
lrh = lrh_next;
}
}
/*
* Encapsulates data into IEEE 802.15.4 frames.
* Fragments an IPv6 datagram into 6LowPAN units, which fit into IEEE 802.15.4 frames.
* If configured, will compress IPv6 and or UDP headers.
* */
static err_t
lowpan6_frag(struct netif *netif, struct pbuf *p, const struct lowpan6_link_addr *src, const struct lowpan6_link_addr *dst)
{
struct pbuf *p_frag;
u16_t frag_len, remaining_len, max_data_len;
u8_t *buffer;
u8_t ieee_header_len;
u8_t lowpan6_header_len;
u8_t hidden_header_len;
u16_t crc;
u16_t datagram_offset;
err_t err = ERR_IF;
LWIP_ASSERT("lowpan6_frag: netif->linkoutput not set", netif->linkoutput != NULL);
/* We'll use a dedicated pbuf for building 6LowPAN fragments. */
p_frag = pbuf_alloc(PBUF_RAW, 127, PBUF_RAM);
if (p_frag == NULL) {
MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
return ERR_MEM;
}
LWIP_ASSERT("this needs a pbuf in one piece", p_frag->len == p_frag->tot_len);
/* Write IEEE 802.15.4 header. */
buffer = (u8_t *)p_frag->payload;
ieee_header_len = lowpan6_write_iee802154_header((struct ieee_802154_hdr *)buffer, src, dst);
LWIP_ASSERT("ieee_header_len < p_frag->len", ieee_header_len < p_frag->len);
#if LWIP_6LOWPAN_IPHC
/* Perform 6LowPAN IPv6 header compression according to RFC 6282 */
/* do the header compression (this does NOT copy any non-compressed data) */
err = lowpan6_compress_headers(netif, (u8_t *)p->payload, p->len,
&buffer[ieee_header_len], p_frag->len - ieee_header_len, &lowpan6_header_len,
&hidden_header_len, LWIP_6LOWPAN_CONTEXTS(netif), src, dst);
if (err != ERR_OK) {
MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
pbuf_free(p_frag);
return err;
}
pbuf_remove_header(p, hidden_header_len);
#else /* LWIP_6LOWPAN_IPHC */
/* Send uncompressed IPv6 header with appropriate dispatch byte. */
lowpan6_header_len = 1;
buffer[ieee_header_len] = 0x41; /* IPv6 dispatch */
#endif /* LWIP_6LOWPAN_IPHC */
/* Calculate remaining packet length */
remaining_len = p->tot_len;
if (remaining_len > 0x7FF) {
MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
/* datagram_size must fit into 11 bit */
pbuf_free(p_frag);
return ERR_VAL;
}
/* Fragment, or 1 packet? */
max_data_len = LOWPAN6_MAX_PAYLOAD - ieee_header_len - lowpan6_header_len;
if (remaining_len > max_data_len) {
u16_t data_len;
/* We must move the 6LowPAN header to make room for the FRAG header. */
memmove(&buffer[ieee_header_len + 4], &buffer[ieee_header_len], lowpan6_header_len);
/* Now we need to fragment the packet. FRAG1 header first */
buffer[ieee_header_len] = 0xc0 | (((p->tot_len + hidden_header_len) >> 8) & 0x7);
buffer[ieee_header_len + 1] = (p->tot_len + hidden_header_len) & 0xff;
lowpan6_data.tx_datagram_tag++;
buffer[ieee_header_len + 2] = (lowpan6_data.tx_datagram_tag >> 8) & 0xff;
buffer[ieee_header_len + 3] = lowpan6_data.tx_datagram_tag & 0xff;
/* Fragment follows. */
data_len = (max_data_len - 4) & 0xf8;
frag_len = data_len + lowpan6_header_len;
pbuf_copy_partial(p, buffer + ieee_header_len + lowpan6_header_len + 4, frag_len - lowpan6_header_len, 0);
remaining_len -= frag_len - lowpan6_header_len;
/* datagram offset holds the offset before compression */
datagram_offset = frag_len - lowpan6_header_len + hidden_header_len;
LWIP_ASSERT("datagram offset must be a multiple of 8", (datagram_offset & 7) == 0);
/* Calculate frame length */
p_frag->len = p_frag->tot_len = ieee_header_len + 4 + frag_len + 2; /* add 2 bytes for crc*/
/* 2 bytes CRC */
crc = LWIP_6LOWPAN_DO_CALC_CRC(p_frag->payload, p_frag->len - 2);
pbuf_take_at(p_frag, &crc, 2, p_frag->len - 2);
/* send the packet */
MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p_frag->tot_len);
LWIP_DEBUGF(LWIP_LOWPAN6_DEBUG | LWIP_DBG_TRACE, ("lowpan6_send: sending packet %p\n", (void *)p));
err = netif->linkoutput(netif, p_frag);
while ((remaining_len > 0) && (err == ERR_OK)) {
struct ieee_802154_hdr *hdr = (struct ieee_802154_hdr *)buffer;
/* new frame, new seq num for ACK */
hdr->sequence_number = lowpan6_data.tx_frame_seq_num++;
buffer[ieee_header_len] |= 0x20; /* Change FRAG1 to FRAGN */
LWIP_ASSERT("datagram offset must be a multiple of 8", (datagram_offset & 7) == 0);
buffer[ieee_header_len + 4] = (u8_t)(datagram_offset >> 3); /* datagram offset in FRAGN header (datagram_offset is max. 11 bit) */
frag_len = (127 - ieee_header_len - 5 - 2) & 0xf8;
if (frag_len > remaining_len) {
frag_len = remaining_len;
}
pbuf_copy_partial(p, buffer + ieee_header_len + 5, frag_len, p->tot_len - remaining_len);
remaining_len -= frag_len;
datagram_offset += frag_len;
/* Calculate frame length */
p_frag->len = p_frag->tot_len = frag_len + 5 + ieee_header_len + 2;
/* 2 bytes CRC */
crc = LWIP_6LOWPAN_DO_CALC_CRC(p_frag->payload, p_frag->len - 2);
pbuf_take_at(p_frag, &crc, 2, p_frag->len - 2);
/* send the packet */
MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p_frag->tot_len);
LWIP_DEBUGF(LWIP_LOWPAN6_DEBUG | LWIP_DBG_TRACE, ("lowpan6_send: sending packet %p\n", (void *)p));
err = netif->linkoutput(netif, p_frag);
}
} else {
/* It fits in one frame. */
frag_len = remaining_len;
/* Copy IPv6 packet */
pbuf_copy_partial(p, buffer + ieee_header_len + lowpan6_header_len, frag_len, 0);
remaining_len = 0;
/* Calculate frame length */
p_frag->len = p_frag->tot_len = frag_len + lowpan6_header_len + ieee_header_len + 2;
LWIP_ASSERT("", p_frag->len <= 127);
/* 2 bytes CRC */
crc = LWIP_6LOWPAN_DO_CALC_CRC(p_frag->payload, p_frag->len - 2);
pbuf_take_at(p_frag, &crc, 2, p_frag->len - 2);
/* send the packet */
MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p_frag->tot_len);
LWIP_DEBUGF(LWIP_LOWPAN6_DEBUG | LWIP_DBG_TRACE, ("lowpan6_send: sending packet %p\n", (void *)p));
err = netif->linkoutput(netif, p_frag);
}
pbuf_free(p_frag);
return err;
}
/**
* @ingroup sixlowpan
* Set context
*/
err_t
lowpan6_set_context(u8_t idx, const ip6_addr_t *context)
{
#if LWIP_6LOWPAN_NUM_CONTEXTS > 0
if (idx >= LWIP_6LOWPAN_NUM_CONTEXTS) {
return ERR_ARG;
}
IP6_ADDR_ZONECHECK(context);
ip6_addr_set(&lowpan6_data.lowpan6_context[idx], context);
return ERR_OK;
#else
LWIP_UNUSED_ARG(idx);
LWIP_UNUSED_ARG(context);
return ERR_ARG;
#endif
}
#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS
/**
* @ingroup sixlowpan
* Set short address
*/
err_t
lowpan6_set_short_addr(u8_t addr_high, u8_t addr_low)
{
short_mac_addr.addr[0] = addr_high;
short_mac_addr.addr[1] = addr_low;
return ERR_OK;
}
#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */
/* Create IEEE 802.15.4 address from netif address */
static err_t
lowpan6_hwaddr_to_addr(struct netif *netif, struct lowpan6_link_addr *addr)
{
addr->addr_len = 8;
if (netif->hwaddr_len == 8) {
LWIP_ERROR("NETIF_MAX_HWADDR_LEN >= 8 required", sizeof(netif->hwaddr) >= 8, return ERR_VAL;);
SMEMCPY(addr->addr, netif->hwaddr, 8);
} else if (netif->hwaddr_len == 6) {
/* Copy from MAC-48 */
SMEMCPY(addr->addr, netif->hwaddr, 3);
addr->addr[3] = addr->addr[4] = 0xff;
SMEMCPY(&addr->addr[5], &netif->hwaddr[3], 3);
} else {
/* Invalid address length, don't know how to convert this */
return ERR_VAL;
}
return ERR_OK;
}
/**
* @ingroup sixlowpan
* Resolve and fill-in IEEE 802.15.4 address header for outgoing IPv6 packet.
*
* Perform Header Compression and fragment if necessary.
*
* @param netif The lwIP network interface which the IP packet will be sent on.
* @param q The pbuf(s) containing the IP packet to be sent.
* @param ip6addr The IP address of the packet destination.
*
* @return err_t
*/
err_t
lowpan6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr)
{
err_t result;
const u8_t *hwaddr;
struct lowpan6_link_addr src, dest;
#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS
ip6_addr_t ip6_src;
struct ip6_hdr *ip6_hdr;
#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */
#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS
/* Check if we can compress source address (use aligned copy) */
ip6_hdr = (struct ip6_hdr *)q->payload;
ip6_addr_copy_from_packed(ip6_src, ip6_hdr->src);
ip6_addr_assign_zone(&ip6_src, IP6_UNICAST, netif);
if (lowpan6_get_address_mode(&ip6_src, &short_mac_addr) == 3) {
src.addr_len = 2;
src.addr[0] = short_mac_addr.addr[0];
src.addr[1] = short_mac_addr.addr[1];
} else
#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */
{
result = lowpan6_hwaddr_to_addr(netif, &src);
if (result != ERR_OK) {
MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
return result;
}
}
/* multicast destination IP address? */
if (ip6_addr_ismulticast(ip6addr)) {
MIB2_STATS_NETIF_INC(netif, ifoutnucastpkts);
/* We need to send to the broadcast address.*/
return lowpan6_frag(netif, q, &src, &ieee_802154_broadcast);
}
/* We have a unicast destination IP address */
/* @todo anycast? */
#if LWIP_6LOWPAN_INFER_SHORT_ADDRESS
if (src.addr_len == 2) {
/* If source address was compressable to short_mac_addr, and dest has same subnet and
* is also compressable to 2-bytes, assume we can infer dest as a short address too. */
dest.addr_len = 2;
dest.addr[0] = ((u8_t *)q->payload)[38];
dest.addr[1] = ((u8_t *)q->payload)[39];
if ((src.addr_len == 2) && (ip6_addr_netcmp_zoneless(&ip6_hdr->src, &ip6_hdr->dest)) &&
(lowpan6_get_address_mode(ip6addr, &dest) == 3)) {
MIB2_STATS_NETIF_INC(netif, ifoutucastpkts);
return lowpan6_frag(netif, q, &src, &dest);
}
}
#endif /* LWIP_6LOWPAN_INFER_SHORT_ADDRESS */
/* Ask ND6 what to do with the packet. */
result = nd6_get_next_hop_addr_or_queue(netif, q, ip6addr, &hwaddr);
if (result != ERR_OK) {
MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
return result;
}
/* If no hardware address is returned, nd6 has queued the packet for later. */
if (hwaddr == NULL) {
return ERR_OK;
}
/* Send out the packet using the returned hardware address. */
dest.addr_len = netif->hwaddr_len;
/* XXX: Inferring the length of the source address from the destination address
* is not correct for IEEE 802.15.4, but currently we don't get this information
* from the neighbor cache */
SMEMCPY(dest.addr, hwaddr, netif->hwaddr_len);
MIB2_STATS_NETIF_INC(netif, ifoutucastpkts);
return lowpan6_frag(netif, q, &src, &dest);
}
/**
* @ingroup sixlowpan
* NETIF input function: don't free the input pbuf when returning != ERR_OK!
*/
err_t
lowpan6_input(struct pbuf *p, struct netif *netif)
{
u8_t *puc, b;
s8_t i;
struct lowpan6_link_addr src, dest;
u16_t datagram_size = 0;
u16_t datagram_offset, datagram_tag;
struct lowpan6_reass_helper *lrh, *lrh_next, *lrh_prev = NULL;
if (p == NULL) {
return ERR_OK;
}
MIB2_STATS_NETIF_ADD(netif, ifinoctets, p->tot_len);
if (p->len != p->tot_len) {
/* for now, this needs a pbuf in one piece */
goto lowpan6_input_discard;
}
if (lowpan6_parse_iee802154_header(p, &src, &dest) != ERR_OK) {
goto lowpan6_input_discard;
}
/* Check dispatch. */
puc = (u8_t *)p->payload;
b = *puc;
if ((b & 0xf8) == 0xc0) {
/* FRAG1 dispatch. add this packet to reassembly list. */
datagram_size = ((u16_t)(puc[0] & 0x07) << 8) | (u16_t)puc[1];
datagram_tag = ((u16_t)puc[2] << 8) | (u16_t)puc[3];
/* check for duplicate */
lrh = lowpan6_data.reass_list;
while (lrh != NULL) {
uint8_t discard = 0;
lrh_next = lrh->next_packet;
if ((lrh->sender_addr.addr_len == src.addr_len) &&
(memcmp(lrh->sender_addr.addr, src.addr, src.addr_len) == 0)) {
/* address match with packet in reassembly. */
if ((datagram_tag == lrh->datagram_tag) && (datagram_size == lrh->datagram_size)) {
/* duplicate fragment. */
goto lowpan6_input_discard;
} else {
/* We are receiving the start of a new datagram. Discard old one (incomplete). */
discard = 1;
}
}
if (discard) {
dequeue_datagram(lrh, lrh_prev);
free_reass_datagram(lrh);
} else {
lrh_prev = lrh;
}
/* Check next datagram in queue. */
lrh = lrh_next;
}
pbuf_remove_header(p, 4); /* hide frag1 dispatch */
lrh = (struct lowpan6_reass_helper *) mem_malloc(sizeof(struct lowpan6_reass_helper));
if (lrh == NULL) {
goto lowpan6_input_discard;
}
lrh->sender_addr.addr_len = src.addr_len;
for (i = 0; i < src.addr_len; i++) {
lrh->sender_addr.addr[i] = src.addr[i];
}
lrh->datagram_size = datagram_size;
lrh->datagram_tag = datagram_tag;
lrh->frags = NULL;
if (*(u8_t *)p->payload == 0x41) {
/* This is a complete IPv6 packet, just skip dispatch byte. */
pbuf_remove_header(p, 1); /* hide dispatch byte. */
lrh->reass = p;
} else if ((*(u8_t *)p->payload & 0xe0 ) == 0x60) {
lrh->reass = lowpan6_decompress(p, datagram_size, LWIP_6LOWPAN_CONTEXTS(netif), &src, &dest);
if (lrh->reass == NULL) {
/* decompression failed */
mem_free(lrh);
goto lowpan6_input_discard;
}
}
/* TODO: handle the case where we already have FRAGN received */
lrh->next_packet = lowpan6_data.reass_list;
lrh->timer = 2;
lowpan6_data.reass_list = lrh;
return ERR_OK;
} else if ((b & 0xf8) == 0xe0) {
/* FRAGN dispatch, find packet being reassembled. */
datagram_size = ((u16_t)(puc[0] & 0x07) << 8) | (u16_t)puc[1];
datagram_tag = ((u16_t)puc[2] << 8) | (u16_t)puc[3];
datagram_offset = (u16_t)puc[4] << 3;
pbuf_remove_header(p, 4); /* hide frag1 dispatch but keep datagram offset for reassembly */
for (lrh = lowpan6_data.reass_list; lrh != NULL; lrh_prev = lrh, lrh = lrh->next_packet) {
if ((lrh->sender_addr.addr_len == src.addr_len) &&
(memcmp(lrh->sender_addr.addr, src.addr, src.addr_len) == 0) &&
(datagram_tag == lrh->datagram_tag) &&
(datagram_size == lrh->datagram_size)) {
break;
}
}
if (lrh == NULL) {
/* rogue fragment */
goto lowpan6_input_discard;
}
/* Insert new pbuf into list of fragments. Each fragment is a pbuf,
this only works for unchained pbufs. */
LWIP_ASSERT("p->next == NULL", p->next == NULL);
if (lrh->reass != NULL) {
/* FRAG1 already received, check this offset against first len */
if (datagram_offset < lrh->reass->len) {
/* fragment overlap, discard old fragments */
dequeue_datagram(lrh, lrh_prev);
free_reass_datagram(lrh);
goto lowpan6_input_discard;
}
}
if (lrh->frags == NULL) {
/* first FRAGN */
lrh->frags = p;
} else {
/* find the correct place to insert */
struct pbuf *q, *last;
u16_t new_frag_len = p->len - 1; /* p->len includes datagram_offset byte */
for (q = lrh->frags, last = NULL; q != NULL; last = q, q = q->next) {
u16_t q_datagram_offset = ((u8_t *)q->payload)[0] << 3;
u16_t q_frag_len = q->len - 1;
if (datagram_offset < q_datagram_offset) {
if (datagram_offset + new_frag_len > q_datagram_offset) {
/* overlap, discard old fragments */
dequeue_datagram(lrh, lrh_prev);
free_reass_datagram(lrh);
goto lowpan6_input_discard;
}
/* insert here */
break;
} else if (datagram_offset == q_datagram_offset) {
if (q_frag_len != new_frag_len) {
/* fragment mismatch, discard old fragments */
dequeue_datagram(lrh, lrh_prev);
free_reass_datagram(lrh);
goto lowpan6_input_discard;
}
/* duplicate, ignore */
pbuf_free(p);
return ERR_OK;
}
}
/* insert fragment */
if (last == NULL) {
lrh->frags = p;
} else {
last->next = p;
p->next = q;
}
}
/* check if all fragments were received */
if (lrh->reass) {
u16_t offset = lrh->reass->len;
struct pbuf *q;
for (q = lrh->frags; q != NULL; q = q->next) {
u16_t q_datagram_offset = ((u8_t *)q->payload)[0] << 3;
if (q_datagram_offset != offset) {
/* not complete, wait for more fragments */
return ERR_OK;
}
offset += q->len - 1;
}
if (offset == datagram_size) {
/* all fragments received, combine pbufs */
u16_t datagram_left = datagram_size - lrh->reass->len;
for (q = lrh->frags; q != NULL; q = q->next) {
/* hide datagram_offset byte now */
pbuf_remove_header(q, 1);
q->tot_len = datagram_left;
datagram_left -= q->len;
}
LWIP_ASSERT("datagram_left == 0", datagram_left == 0);
q = lrh->reass;
q->tot_len = datagram_size;
q->next = lrh->frags;
lrh->frags = NULL;
lrh->reass = NULL;
dequeue_datagram(lrh, lrh_prev);
mem_free(lrh);
/* @todo: distinguish unicast/multicast */
MIB2_STATS_NETIF_INC(netif, ifinucastpkts);
return ip6_input(q, netif);
}
}
/* pbuf enqueued, waiting for more fragments */
return ERR_OK;
} else {
if (b == 0x41) {
/* This is a complete IPv6 packet, just skip dispatch byte. */
pbuf_remove_header(p, 1); /* hide dispatch byte. */
} else if ((b & 0xe0 ) == 0x60) {
/* IPv6 headers are compressed using IPHC. */
p = lowpan6_decompress(p, datagram_size, LWIP_6LOWPAN_CONTEXTS(netif), &src, &dest);
if (p == NULL) {
MIB2_STATS_NETIF_INC(netif, ifindiscards);
return ERR_OK;
}
} else {
goto lowpan6_input_discard;
}
/* @todo: distinguish unicast/multicast */
MIB2_STATS_NETIF_INC(netif, ifinucastpkts);
return ip6_input(p, netif);
}
lowpan6_input_discard:
MIB2_STATS_NETIF_INC(netif, ifindiscards);
pbuf_free(p);
/* always return ERR_OK here to prevent the caller freeing the pbuf */
return ERR_OK;
}
/**
* @ingroup sixlowpan
*/
err_t
lowpan6_if_init(struct netif *netif)
{
netif->name[0] = 'L';
netif->name[1] = '6';
netif->output_ip6 = lowpan6_output;
MIB2_INIT_NETIF(netif, snmp_ifType_other, 0);
/* maximum transfer unit */
netif->mtu = 1280;
/* broadcast capability */
netif->flags = NETIF_FLAG_BROADCAST /* | NETIF_FLAG_LOWPAN6 */;
return ERR_OK;
}
/**
* @ingroup sixlowpan
* Set PAN ID
*/
err_t
lowpan6_set_pan_id(u16_t pan_id)
{
lowpan6_data.ieee_802154_pan_id = pan_id;
return ERR_OK;
}
#if !NO_SYS
/**
* @ingroup sixlowpan
* Pass a received packet to tcpip_thread for input processing
*
* @param p the received packet, p->payload pointing to the
* IEEE 802.15.4 header.
* @param inp the network interface on which the packet was received
*/
err_t
tcpip_6lowpan_input(struct pbuf *p, struct netif *inp)
{
return tcpip_inpkt(p, inp, lowpan6_input);
}
#endif /* !NO_SYS */
#endif /* LWIP_IPV6 */

View File

@@ -0,0 +1,447 @@
/**
* @file
* 6LowPAN over BLE output for IPv6 (RFC7668).
*/
/*
* Copyright (c) 2017 Benjamin Aigner
* Copyright (c) 2015 Inico Technologies Ltd. , Author: Ivan Delamer <delamer@inicotech.com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
*
* Author: Benjamin Aigner <aignerb@technikum-wien.at>
*
* Based on the original 6lowpan implementation of lwIP ( @see 6lowpan.c)
*/
/**
* @defgroup rfc7668if 6LoWPAN over BLE (RFC7668)
* @ingroup netifs
* This file implements a RFC7668 implementation for 6LoWPAN over
* Bluetooth Low Energy. The specification is very similar to 6LoWPAN,
* so most of the code is re-used.
* Compared to 6LoWPAN, much functionality is already implemented in
* lower BLE layers (fragmenting, session management,...).
*
* Usage:
* - add this netif
* - don't add IPv4 addresses (no IPv4 support in RFC7668), pass 'NULL','NULL','NULL'
* - use the BLE to EUI64 conversation util to create an IPv6 link-local address from the BLE MAC (@ref ble_addr_to_eui64)
* - input function: @ref rfc7668_input
* - set the link output function, which transmits output data to an established L2CAP channel
* - If data arrives (HCI event "L2CAP_DATA_PACKET"):
* - allocate a @ref PBUF_RAW buffer
* - let the pbuf struct point to the incoming data or copy it to the buffer
* - call netif->input
*
* @todo:
* - further testing
* - support compression contexts
* - support multiple addresses
* - support multicast
* - support neighbor discovery
*/
#include "netif/lowpan6_ble.h"
#if LWIP_IPV6
#include "lwip/ip.h"
#include "lwip/pbuf.h"
#include "lwip/ip_addr.h"
#include "lwip/netif.h"
#include "lwip/nd6.h"
#include "lwip/mem.h"
#include "lwip/udp.h"
#include "lwip/tcpip.h"
#include "lwip/snmp.h"
#include <string.h>
#if LWIP_6LOWPAN_NUM_CONTEXTS > 0
/** context memory, containing IPv6 addresses */
static ip6_addr_t rfc7668_context[LWIP_6LOWPAN_NUM_CONTEXTS];
#else
#define rfc7668_context NULL
#endif
static struct lowpan6_link_addr rfc7668_local_addr;
static struct lowpan6_link_addr rfc7668_peer_addr;
/**
* @ingroup rfc7668if
* convert BT address to EUI64 addr
*
* This method converts a Bluetooth MAC address to an EUI64 address,
* which is used within IPv6 communication
*
* @param dst IPv6 destination space
* @param src BLE MAC address source
* @param public_addr If the LWIP_RFC7668_LINUX_WORKAROUND_PUBLIC_ADDRESS
* option is set, bit 0x02 will be set if param=0 (no public addr); cleared otherwise
*
* @see LWIP_RFC7668_LINUX_WORKAROUND_PUBLIC_ADDRESS
*/
void
ble_addr_to_eui64(uint8_t *dst, const uint8_t *src, int public_addr)
{
/* according to RFC7668 ch 3.2.2. */
memcpy(dst, src, 3);
dst[3] = 0xFF;
dst[4] = 0xFE;
memcpy(&dst[5], &src[3], 3);
#if LWIP_RFC7668_LINUX_WORKAROUND_PUBLIC_ADDRESS
if(public_addr) {
dst[0] &= ~0x02;
} else {
dst[0] |= 0x02;
}
#else
LWIP_UNUSED_ARG(public_addr);
#endif
}
/**
* @ingroup rfc7668if
* convert EUI64 address to Bluetooth MAC addr
*
* This method converts an EUI64 address to a Bluetooth MAC address,
*
* @param dst BLE MAC address destination
* @param src IPv6 source
*
*/
void
eui64_to_ble_addr(uint8_t *dst, const uint8_t *src)
{
/* according to RFC7668 ch 3.2.2. */
memcpy(dst,src,3);
memcpy(&dst[3],&src[5],3);
}
/** Set an address used for stateful compression.
* This expects an address of 6 or 8 bytes.
*/
static err_t
rfc7668_set_addr(struct lowpan6_link_addr *addr, const u8_t *in_addr, size_t in_addr_len, int is_mac_48, int is_public_addr)
{
if ((in_addr == NULL) || (addr == NULL)) {
return ERR_VAL;
}
if (is_mac_48) {
if (in_addr_len != 6) {
return ERR_VAL;
}
addr->addr_len = 8;
ble_addr_to_eui64(addr->addr, in_addr, is_public_addr);
} else {
if (in_addr_len != 8) {
return ERR_VAL;
}
addr->addr_len = 8;
memcpy(addr->addr, in_addr, 8);
}
return ERR_OK;
}
/** Set the local address used for stateful compression.
* This expects an address of 8 bytes.
*/
err_t
rfc7668_set_local_addr_eui64(struct netif *netif, const u8_t *local_addr, size_t local_addr_len)
{
/* netif not used for now, the address is stored globally... */
LWIP_UNUSED_ARG(netif);
return rfc7668_set_addr(&rfc7668_local_addr, local_addr, local_addr_len, 0, 0);
}
/** Set the local address used for stateful compression.
* This expects an address of 6 bytes.
*/
err_t
rfc7668_set_local_addr_mac48(struct netif *netif, const u8_t *local_addr, size_t local_addr_len, int is_public_addr)
{
/* netif not used for now, the address is stored globally... */
LWIP_UNUSED_ARG(netif);
return rfc7668_set_addr(&rfc7668_local_addr, local_addr, local_addr_len, 1, is_public_addr);
}
/** Set the peer address used for stateful compression.
* This expects an address of 8 bytes.
*/
err_t
rfc7668_set_peer_addr_eui64(struct netif *netif, const u8_t *peer_addr, size_t peer_addr_len)
{
/* netif not used for now, the address is stored globally... */
LWIP_UNUSED_ARG(netif);
return rfc7668_set_addr(&rfc7668_peer_addr, peer_addr, peer_addr_len, 0, 0);
}
/** Set the peer address used for stateful compression.
* This expects an address of 6 bytes.
*/
err_t
rfc7668_set_peer_addr_mac48(struct netif *netif, const u8_t *peer_addr, size_t peer_addr_len, int is_public_addr)
{
/* netif not used for now, the address is stored globally... */
LWIP_UNUSED_ARG(netif);
return rfc7668_set_addr(&rfc7668_peer_addr, peer_addr, peer_addr_len, 1, is_public_addr);
}
/** Encapsulate IPv6 frames for BLE transmission
*
* This method implements the IPv6 header compression:
* *) According to RFC6282
* *) See Figure 2, contains base format of bit positions
* *) Fragmentation not necessary (done at L2CAP layer of BLE)
* @note Currently the pbuf allocation uses 256 bytes. If longer packets are used (possible due to MTU=1480Bytes), increase it here!
*
* @param p Pbuf struct, containing the payload data
* @param netif Output network interface. Should be of RFC7668 type
*
* @return Same as netif->output.
*/
static err_t
rfc7668_compress(struct netif *netif, struct pbuf *p)
{
struct pbuf *p_frag;
u16_t remaining_len;
u8_t *buffer;
u8_t lowpan6_header_len;
u8_t hidden_header_len;
err_t err;
LWIP_ASSERT("lowpan6_frag: netif->linkoutput not set", netif->linkoutput != NULL);
#if LWIP_6LOWPAN_IPHC
/* We'll use a dedicated pbuf for building BLE fragments.
* We'll over-allocate it by the bytes saved for header compression.
*/
p_frag = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
if (p_frag == NULL) {
MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
return ERR_MEM;
}
LWIP_ASSERT("this needs a pbuf in one piece", p_frag->len == p_frag->tot_len);
/* Write IP6 header (with IPHC). */
buffer = (u8_t*)p_frag->payload;
err = lowpan6_compress_headers(netif, (u8_t *)p->payload, p->len, buffer, p_frag->len,
&lowpan6_header_len, &hidden_header_len, rfc7668_context, &rfc7668_local_addr, &rfc7668_peer_addr);
if (err != ERR_OK) {
MIB2_STATS_NETIF_INC(netif, ifoutdiscards);
pbuf_free(p_frag);
return err;
}
pbuf_remove_header(p, hidden_header_len);
/* Calculate remaining packet length */
remaining_len = p->tot_len;
/* Copy IPv6 packet */
pbuf_copy_partial(p, buffer + lowpan6_header_len, remaining_len, 0);
/* Calculate frame length */
p_frag->len = p_frag->tot_len = remaining_len + lowpan6_header_len;
/* send the packet */
MIB2_STATS_NETIF_ADD(netif, ifoutoctets, p_frag->tot_len);
LWIP_DEBUGF(LWIP_LOWPAN6_DEBUG|LWIP_DBG_TRACE, ("rfc7668_output: sending packet %p\n", (void *)p));
err = netif->linkoutput(netif, p_frag);
pbuf_free(p_frag);
return err;
#else /* LWIP_6LOWPAN_IPHC */
/* 6LoWPAN over BLE requires IPHC! */
return ERR_IF;
#endif/* LWIP_6LOWPAN_IPHC */
}
/**
* @ingroup rfc7668if
* Set context id IPv6 address
*
* Store one IPv6 address to a given context id.
*
* @param idx Context id
* @param context IPv6 addr for this context
*
* @return ERR_OK (if everything is fine), ERR_ARG (if the context id is out of range), ERR_VAL (if contexts disabled)
*/
err_t
rfc7668_set_context(u8_t idx, const ip6_addr_t *context)
{
#if LWIP_6LOWPAN_NUM_CONTEXTS > 0
/* check if the ID is possible */
if (idx >= LWIP_6LOWPAN_NUM_CONTEXTS) {
return ERR_ARG;
}
/* copy IPv6 address to context storage */
ip6_addr_set(&rfc7668_context[idx], context);
return ERR_OK;
#else
LWIP_UNUSED_ARG(idx);
LWIP_UNUSED_ARG(context);
return ERR_VAL;
#endif
}
/**
* @ingroup rfc7668if
* Compress outgoing IPv6 packet and pass it on to netif->linkoutput
*
* @param netif The lwIP network interface which the IP packet will be sent on.
* @param q The pbuf(s) containing the IP packet to be sent.
* @param ip6addr The IP address of the packet destination.
*
* @return See rfc7668_compress
*/
err_t
rfc7668_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr)
{
/* dst ip6addr is not used here, we only have one peer */
LWIP_UNUSED_ARG(ip6addr);
return rfc7668_compress(netif, q);
}
/**
* @ingroup rfc7668if
* Process a received raw payload from an L2CAP channel
*
* @param p the received packet, p->payload pointing to the
* IPv6 header (maybe compressed)
* @param netif the network interface on which the packet was received
*
* @return ERR_OK if everything was fine
*/
err_t
rfc7668_input(struct pbuf * p, struct netif *netif)
{
u8_t * puc;
MIB2_STATS_NETIF_ADD(netif, ifinoctets, p->tot_len);
/* Load first header byte */
puc = (u8_t*)p->payload;
/* no IP header compression */
if (*puc == 0x41) {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Completed packet, removing dispatch: 0x%2x \n", *puc));
/* This is a complete IPv6 packet, just skip header byte. */
pbuf_remove_header(p, 1);
/* IPHC header compression */
} else if ((*puc & 0xe0 )== 0x60) {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Completed packet, decompress dispatch: 0x%2x \n", *puc));
/* IPv6 headers are compressed using IPHC. */
p = lowpan6_decompress(p, 0, rfc7668_context, &rfc7668_peer_addr, &rfc7668_local_addr);
/* if no pbuf is returned, handle as discarded packet */
if (p == NULL) {
MIB2_STATS_NETIF_INC(netif, ifindiscards);
return ERR_OK;
}
/* invalid header byte, discard */
} else {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Completed packet, discarding: 0x%2x \n", *puc));
MIB2_STATS_NETIF_INC(netif, ifindiscards);
pbuf_free(p);
return ERR_OK;
}
/* @todo: distinguish unicast/multicast */
MIB2_STATS_NETIF_INC(netif, ifinucastpkts);
#if LWIP_RFC7668_IP_UNCOMPRESSED_DEBUG
{
u16_t i;
LWIP_DEBUGF(LWIP_RFC7668_IP_UNCOMPRESSED_DEBUG, ("IPv6 payload:\n"));
for (i = 0; i < p->len; i++) {
if ((i%4)==0) {
LWIP_DEBUGF(LWIP_RFC7668_IP_UNCOMPRESSED_DEBUG, ("\n"));
}
LWIP_DEBUGF(LWIP_RFC7668_IP_UNCOMPRESSED_DEBUG, ("%2X ", *((uint8_t *)p->payload+i)));
}
LWIP_DEBUGF(LWIP_RFC7668_IP_UNCOMPRESSED_DEBUG, ("\np->len: %d\n", p->len));
}
#endif
/* pass data to ip6_input */
return ip6_input(p, netif);
}
/**
* @ingroup rfc7668if
* Initialize the netif
*
* No flags are used (broadcast not possible, not ethernet, ...)
* The shortname for this netif is "BT"
*
* @param netif the network interface to be initialized as RFC7668 netif
*
* @return ERR_OK if everything went fine
*/
err_t
rfc7668_if_init(struct netif *netif)
{
netif->name[0] = 'b';
netif->name[1] = 't';
/* local function as IPv6 output */
netif->output_ip6 = rfc7668_output;
MIB2_INIT_NETIF(netif, snmp_ifType_other, 0);
/* maximum transfer unit, set according to RFC7668 ch2.4 */
netif->mtu = 1280;
/* no flags set (no broadcast, ethernet,...)*/
netif->flags = 0;
/* everything fine */
return ERR_OK;
}
#if !NO_SYS
/**
* Pass a received packet to tcpip_thread for input processing
*
* @param p the received packet, p->payload pointing to the
* IEEE 802.15.4 header.
* @param inp the network interface on which the packet was received
*
* @return see @ref tcpip_inpkt, same return values
*/
err_t
tcpip_rfc7668_input(struct pbuf *p, struct netif *inp)
{
/* send data to upper layer, return the result */
return tcpip_inpkt(p, inp, rfc7668_input);
}
#endif /* !NO_SYS */
#endif /* LWIP_IPV6 */

View File

@@ -0,0 +1,841 @@
/**
* @file
*
* Common 6LowPAN routines for IPv6. Uses ND tables for link-layer addressing. Fragments packets to 6LowPAN units.
*
* This implementation aims to conform to IEEE 802.15.4(-2015), RFC 4944 and RFC 6282.
* @todo: RFC 6775.
*/
/*
* Copyright (c) 2015 Inico Technologies Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Ivan Delamer <delamer@inicotech.com>
*
*
* Please coordinate changes and requests with Ivan Delamer
* <delamer@inicotech.com>
*/
/**
* @defgroup sixlowpan 6LoWPAN (RFC4944)
* @ingroup netifs
* 6LowPAN netif implementation
*/
#include "netif/lowpan6_common.h"
#if LWIP_IPV6
#include "lwip/ip.h"
#include "lwip/pbuf.h"
#include "lwip/ip_addr.h"
#include "lwip/netif.h"
#include "lwip/udp.h"
#include <string.h>
/* Determine compression mode for unicast address. */
s8_t
lowpan6_get_address_mode(const ip6_addr_t *ip6addr, const struct lowpan6_link_addr *mac_addr)
{
if (mac_addr->addr_len == 2) {
if ((ip6addr->addr[2] == (u32_t)PP_HTONL(0x000000ff)) &&
((ip6addr->addr[3] & PP_HTONL(0xffff0000)) == PP_NTOHL(0xfe000000))) {
if ((ip6addr->addr[3] & PP_HTONL(0x0000ffff)) == lwip_ntohl((mac_addr->addr[0] << 8) | mac_addr->addr[1])) {
return 3;
}
}
} else if (mac_addr->addr_len == 8) {
if ((ip6addr->addr[2] == lwip_ntohl(((mac_addr->addr[0] ^ 2) << 24) | (mac_addr->addr[1] << 16) | mac_addr->addr[2] << 8 | mac_addr->addr[3])) &&
(ip6addr->addr[3] == lwip_ntohl((mac_addr->addr[4] << 24) | (mac_addr->addr[5] << 16) | mac_addr->addr[6] << 8 | mac_addr->addr[7]))) {
return 3;
}
}
if ((ip6addr->addr[2] == PP_HTONL(0x000000ffUL)) &&
((ip6addr->addr[3] & PP_HTONL(0xffff0000)) == PP_NTOHL(0xfe000000UL))) {
return 2;
}
return 1;
}
#if LWIP_6LOWPAN_IPHC
/* Determine compression mode for multicast address. */
static s8_t
lowpan6_get_address_mode_mc(const ip6_addr_t *ip6addr)
{
if ((ip6addr->addr[0] == PP_HTONL(0xff020000)) &&
(ip6addr->addr[1] == 0) &&
(ip6addr->addr[2] == 0) &&
((ip6addr->addr[3] & PP_HTONL(0xffffff00)) == 0)) {
return 3;
} else if (((ip6addr->addr[0] & PP_HTONL(0xff00ffff)) == PP_HTONL(0xff000000)) &&
(ip6addr->addr[1] == 0)) {
if ((ip6addr->addr[2] == 0) &&
((ip6addr->addr[3] & PP_HTONL(0xff000000)) == 0)) {
return 2;
} else if ((ip6addr->addr[2] & PP_HTONL(0xffffff00)) == 0) {
return 1;
}
}
return 0;
}
#if LWIP_6LOWPAN_NUM_CONTEXTS > 0
static s8_t
lowpan6_context_lookup(const ip6_addr_t *lowpan6_contexts, const ip6_addr_t *ip6addr)
{
s8_t i;
for (i = 0; i < LWIP_6LOWPAN_NUM_CONTEXTS; i++) {
if (ip6_addr_netcmp(&lowpan6_contexts[i], ip6addr)) {
return i;
}
}
return -1;
}
#endif /* LWIP_6LOWPAN_NUM_CONTEXTS > 0 */
/*
* Compress IPv6 and/or UDP headers.
* */
err_t
lowpan6_compress_headers(struct netif *netif, u8_t *inbuf, size_t inbuf_size, u8_t *outbuf, size_t outbuf_size,
u8_t *lowpan6_header_len_out, u8_t *hidden_header_len_out, ip6_addr_t *lowpan6_contexts,
const struct lowpan6_link_addr *src, const struct lowpan6_link_addr *dst)
{
u8_t *buffer, *inptr;
u8_t lowpan6_header_len;
u8_t hidden_header_len = 0;
s8_t i;
struct ip6_hdr *ip6hdr;
ip_addr_t ip6src, ip6dst;
LWIP_ASSERT("netif != NULL", netif != NULL);
LWIP_ASSERT("inbuf != NULL", inbuf != NULL);
LWIP_ASSERT("outbuf != NULL", outbuf != NULL);
LWIP_ASSERT("lowpan6_header_len_out != NULL", lowpan6_header_len_out != NULL);
LWIP_ASSERT("hidden_header_len_out != NULL", hidden_header_len_out != NULL);
/* Perform 6LowPAN IPv6 header compression according to RFC 6282 */
buffer = outbuf;
inptr = inbuf;
if (inbuf_size < IP6_HLEN) {
/* input buffer too short */
return ERR_VAL;
}
if (outbuf_size < IP6_HLEN) {
/* output buffer too short for worst case */
return ERR_MEM;
}
/* Point to ip6 header and align copies of src/dest addresses. */
ip6hdr = (struct ip6_hdr *)inptr;
ip_addr_copy_from_ip6_packed(ip6dst, ip6hdr->dest);
ip6_addr_assign_zone(ip_2_ip6(&ip6dst), IP6_UNKNOWN, netif);
ip_addr_copy_from_ip6_packed(ip6src, ip6hdr->src);
ip6_addr_assign_zone(ip_2_ip6(&ip6src), IP6_UNKNOWN, netif);
/* Basic length of 6LowPAN header, set dispatch and clear fields. */
lowpan6_header_len = 2;
buffer[0] = 0x60;
buffer[1] = 0;
/* Determine whether there will be a Context Identifier Extension byte or not.
* If so, set it already. */
#if LWIP_6LOWPAN_NUM_CONTEXTS > 0
buffer[2] = 0;
i = lowpan6_context_lookup(lowpan6_contexts, ip_2_ip6(&ip6src));
if (i >= 0) {
/* Stateful source address compression. */
buffer[1] |= 0x40;
buffer[2] |= (i & 0x0f) << 4;
}
i = lowpan6_context_lookup(lowpan6_contexts, ip_2_ip6(&ip6dst));
if (i >= 0) {
/* Stateful destination address compression. */
buffer[1] |= 0x04;
buffer[2] |= i & 0x0f;
}
if (buffer[2] != 0x00) {
/* Context identifier extension byte is appended. */
buffer[1] |= 0x80;
lowpan6_header_len++;
}
#else /* LWIP_6LOWPAN_NUM_CONTEXTS > 0 */
LWIP_UNUSED_ARG(lowpan6_contexts);
#endif /* LWIP_6LOWPAN_NUM_CONTEXTS > 0 */
/* Determine TF field: Traffic Class, Flow Label */
if (IP6H_FL(ip6hdr) == 0) {
/* Flow label is elided. */
buffer[0] |= 0x10;
if (IP6H_TC(ip6hdr) == 0) {
/* Traffic class (ECN+DSCP) elided too. */
buffer[0] |= 0x08;
} else {
/* Traffic class (ECN+DSCP) appended. */
buffer[lowpan6_header_len++] = IP6H_TC(ip6hdr);
}
} else {
if (((IP6H_TC(ip6hdr) & 0x3f) == 0)) {
/* DSCP portion of Traffic Class is elided, ECN and FL are appended (3 bytes) */
buffer[0] |= 0x08;
buffer[lowpan6_header_len] = IP6H_TC(ip6hdr) & 0xc0;
buffer[lowpan6_header_len++] |= (IP6H_FL(ip6hdr) >> 16) & 0x0f;
buffer[lowpan6_header_len++] = (IP6H_FL(ip6hdr) >> 8) & 0xff;
buffer[lowpan6_header_len++] = IP6H_FL(ip6hdr) & 0xff;
} else {
/* Traffic class and flow label are appended (4 bytes) */
buffer[lowpan6_header_len++] = IP6H_TC(ip6hdr);
buffer[lowpan6_header_len++] = (IP6H_FL(ip6hdr) >> 16) & 0x0f;
buffer[lowpan6_header_len++] = (IP6H_FL(ip6hdr) >> 8) & 0xff;
buffer[lowpan6_header_len++] = IP6H_FL(ip6hdr) & 0xff;
}
}
/* Compress NH?
* Only if UDP for now. @todo support other NH compression. */
if (IP6H_NEXTH(ip6hdr) == IP6_NEXTH_UDP) {
buffer[0] |= 0x04;
} else {
/* append nexth. */
buffer[lowpan6_header_len++] = IP6H_NEXTH(ip6hdr);
}
/* Compress hop limit? */
if (IP6H_HOPLIM(ip6hdr) == 255) {
buffer[0] |= 0x03;
} else if (IP6H_HOPLIM(ip6hdr) == 64) {
buffer[0] |= 0x02;
} else if (IP6H_HOPLIM(ip6hdr) == 1) {
buffer[0] |= 0x01;
} else {
/* append hop limit */
buffer[lowpan6_header_len++] = IP6H_HOPLIM(ip6hdr);
}
/* Compress source address */
if (((buffer[1] & 0x40) != 0) ||
(ip6_addr_islinklocal(ip_2_ip6(&ip6src)))) {
/* Context-based or link-local source address compression. */
i = lowpan6_get_address_mode(ip_2_ip6(&ip6src), src);
buffer[1] |= (i & 0x03) << 4;
if (i == 1) {
MEMCPY(buffer + lowpan6_header_len, inptr + 16, 8);
lowpan6_header_len += 8;
} else if (i == 2) {
MEMCPY(buffer + lowpan6_header_len, inptr + 22, 2);
lowpan6_header_len += 2;
}
} else if (ip6_addr_isany(ip_2_ip6(&ip6src))) {
/* Special case: mark SAC and leave SAM=0 */
buffer[1] |= 0x40;
} else {
/* Append full address. */
MEMCPY(buffer + lowpan6_header_len, inptr + 8, 16);
lowpan6_header_len += 16;
}
/* Compress destination address */
if (ip6_addr_ismulticast(ip_2_ip6(&ip6dst))) {
/* @todo support stateful multicast address compression */
buffer[1] |= 0x08;
i = lowpan6_get_address_mode_mc(ip_2_ip6(&ip6dst));
buffer[1] |= i & 0x03;
if (i == 0) {
MEMCPY(buffer + lowpan6_header_len, inptr + 24, 16);
lowpan6_header_len += 16;
} else if (i == 1) {
buffer[lowpan6_header_len++] = inptr[25];
MEMCPY(buffer + lowpan6_header_len, inptr + 35, 5);
lowpan6_header_len += 5;
} else if (i == 2) {
buffer[lowpan6_header_len++] = inptr[25];
MEMCPY(buffer + lowpan6_header_len, inptr + 37, 3);
lowpan6_header_len += 3;
} else if (i == 3) {
buffer[lowpan6_header_len++] = (inptr)[39];
}
} else if (((buffer[1] & 0x04) != 0) ||
(ip6_addr_islinklocal(ip_2_ip6(&ip6dst)))) {
/* Context-based or link-local destination address compression. */
i = lowpan6_get_address_mode(ip_2_ip6(&ip6dst), dst);
buffer[1] |= i & 0x03;
if (i == 1) {
MEMCPY(buffer + lowpan6_header_len, inptr + 32, 8);
lowpan6_header_len += 8;
} else if (i == 2) {
MEMCPY(buffer + lowpan6_header_len, inptr + 38, 2);
lowpan6_header_len += 2;
}
} else {
/* Append full address. */
MEMCPY(buffer + lowpan6_header_len, inptr + 24, 16);
lowpan6_header_len += 16;
}
/* Move to payload. */
inptr += IP6_HLEN;
hidden_header_len += IP6_HLEN;
#if LWIP_UDP
/* Compress UDP header? */
if (IP6H_NEXTH(ip6hdr) == IP6_NEXTH_UDP) {
/* @todo support optional checksum compression */
if (inbuf_size < IP6_HLEN + UDP_HLEN) {
/* input buffer too short */
return ERR_VAL;
}
if (outbuf_size < (size_t)(hidden_header_len + 7)) {
/* output buffer too short for worst case */
return ERR_MEM;
}
buffer[lowpan6_header_len] = 0xf0;
/* determine port compression mode. */
if ((inptr[0] == 0xf0) && ((inptr[1] & 0xf0) == 0xb0) &&
(inptr[2] == 0xf0) && ((inptr[3] & 0xf0) == 0xb0)) {
/* Compress source and dest ports. */
buffer[lowpan6_header_len++] |= 0x03;
buffer[lowpan6_header_len++] = ((inptr[1] & 0x0f) << 4) | (inptr[3] & 0x0f);
} else if (inptr[0] == 0xf0) {
/* Compress source port. */
buffer[lowpan6_header_len++] |= 0x02;
buffer[lowpan6_header_len++] = inptr[1];
buffer[lowpan6_header_len++] = inptr[2];
buffer[lowpan6_header_len++] = inptr[3];
} else if (inptr[2] == 0xf0) {
/* Compress dest port. */
buffer[lowpan6_header_len++] |= 0x01;
buffer[lowpan6_header_len++] = inptr[0];
buffer[lowpan6_header_len++] = inptr[1];
buffer[lowpan6_header_len++] = inptr[3];
} else {
/* append full ports. */
lowpan6_header_len++;
buffer[lowpan6_header_len++] = inptr[0];
buffer[lowpan6_header_len++] = inptr[1];
buffer[lowpan6_header_len++] = inptr[2];
buffer[lowpan6_header_len++] = inptr[3];
}
/* elide length and copy checksum */
buffer[lowpan6_header_len++] = inptr[6];
buffer[lowpan6_header_len++] = inptr[7];
hidden_header_len += UDP_HLEN;
}
#endif /* LWIP_UDP */
*lowpan6_header_len_out = lowpan6_header_len;
*hidden_header_len_out = hidden_header_len;
return ERR_OK;
}
/** Decompress IPv6 and UDP headers compressed according to RFC 6282
*
* @param lowpan6_buffer compressed headers, first byte is the dispatch byte
* @param lowpan6_bufsize size of lowpan6_buffer (may include data after headers)
* @param decomp_buffer buffer where the decompressed headers are stored
* @param decomp_bufsize size of decomp_buffer
* @param hdr_size_comp returns the size of the compressed headers (skip to get to data)
* @param hdr_size_decomp returns the size of the decompressed headers (IPv6 + UDP)
* @param datagram_size datagram size from fragments or 0 if unfragmented
* @param compressed_size compressed datagram size (for unfragmented rx)
* @param lowpan6_contexts context addresses
* @param src source address of the outer layer, used for address compression
* @param dest destination address of the outer layer, used for address compression
* @return ERR_OK if decompression succeeded, an error otherwise
*/
static err_t
lowpan6_decompress_hdr(u8_t *lowpan6_buffer, size_t lowpan6_bufsize,
u8_t *decomp_buffer, size_t decomp_bufsize,
u16_t *hdr_size_comp, u16_t *hdr_size_decomp,
u16_t datagram_size, u16_t compressed_size,
ip6_addr_t *lowpan6_contexts,
struct lowpan6_link_addr *src, struct lowpan6_link_addr *dest)
{
u16_t lowpan6_offset;
struct ip6_hdr *ip6hdr;
s8_t i;
u32_t header_temp;
u16_t ip6_offset = IP6_HLEN;
LWIP_ASSERT("lowpan6_buffer != NULL", lowpan6_buffer != NULL);
LWIP_ASSERT("decomp_buffer != NULL", decomp_buffer != NULL);
LWIP_ASSERT("src != NULL", src != NULL);
LWIP_ASSERT("dest != NULL", dest != NULL);
LWIP_ASSERT("hdr_size_comp != NULL", hdr_size_comp != NULL);
LWIP_ASSERT("dehdr_size_decompst != NULL", hdr_size_decomp != NULL);
ip6hdr = (struct ip6_hdr *)decomp_buffer;
if (decomp_bufsize < IP6_HLEN) {
return ERR_MEM;
}
/* output the full compressed packet, if set in @see lowpan6_opts.h */
#if LWIP_LOWPAN6_IP_COMPRESSED_DEBUG
{
u16_t j;
LWIP_DEBUGF(LWIP_LOWPAN6_IP_COMPRESSED_DEBUG, ("lowpan6_decompress_hdr: IP6 payload (compressed): \n"));
for (j = 0; j < lowpan6_bufsize; j++) {
if ((j % 4) == 0) {
LWIP_DEBUGF(LWIP_LOWPAN6_IP_COMPRESSED_DEBUG, ("\n"));
}
LWIP_DEBUGF(LWIP_LOWPAN6_IP_COMPRESSED_DEBUG, ("%2X ", lowpan6_buffer[j]));
}
LWIP_DEBUGF(LWIP_LOWPAN6_IP_COMPRESSED_DEBUG, ("\np->len: %d", lowpan6_bufsize));
}
#endif
/* offset for inline IP headers (RFC 6282 ch3)*/
lowpan6_offset = 2;
/* if CID is set (context identifier), the context byte
* follows immediately after the header, so other IPHC fields are @+3 */
if (lowpan6_buffer[1] & 0x80) {
lowpan6_offset++;
}
/* Set IPv6 version, traffic class and flow label. (RFC6282, ch 3.1.1.)*/
if ((lowpan6_buffer[0] & 0x18) == 0x00) {
header_temp = ((lowpan6_buffer[lowpan6_offset+1] & 0x0f) << 16) | \
(lowpan6_buffer[lowpan6_offset + 2] << 8) | lowpan6_buffer[lowpan6_offset+3];
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("TF: 00, ECN: 0x%2x, Flowlabel+DSCP: 0x%8X\n", \
lowpan6_buffer[lowpan6_offset],header_temp));
IP6H_VTCFL_SET(ip6hdr, 6, lowpan6_buffer[lowpan6_offset], header_temp);
/* increase offset, processed 4 bytes here:
* TF=00: ECN + DSCP + 4-bit Pad + Flow Label (4 bytes)*/
lowpan6_offset += 4;
} else if ((lowpan6_buffer[0] & 0x18) == 0x08) {
header_temp = ((lowpan6_buffer[lowpan6_offset] & 0x0f) << 16) | (lowpan6_buffer[lowpan6_offset + 1] << 8) | lowpan6_buffer[lowpan6_offset+2];
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("TF: 01, ECN: 0x%2x, Flowlabel: 0x%2X, DSCP ignored\n", \
lowpan6_buffer[lowpan6_offset] & 0xc0,header_temp));
IP6H_VTCFL_SET(ip6hdr, 6, lowpan6_buffer[lowpan6_offset] & 0xc0, header_temp);
/* increase offset, processed 3 bytes here:
* TF=01: ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided.*/
lowpan6_offset += 3;
} else if ((lowpan6_buffer[0] & 0x18) == 0x10) {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("TF: 10, DCSP+ECN: 0x%2x, Flowlabel ignored\n", lowpan6_buffer[lowpan6_offset]));
IP6H_VTCFL_SET(ip6hdr, 6, lowpan6_buffer[lowpan6_offset],0);
/* increase offset, processed 1 byte here:
* ECN + DSCP (1 byte), Flow Label is elided.*/
lowpan6_offset += 1;
} else if ((lowpan6_buffer[0] & 0x18) == 0x18) {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("TF: 11, DCSP/ECN & Flowlabel ignored\n"));
/* don't increase offset, no bytes processed here */
IP6H_VTCFL_SET(ip6hdr, 6, 0, 0);
}
/* Set Next Header (NH) */
if ((lowpan6_buffer[0] & 0x04) == 0x00) {
/* 0: full next header byte carried inline (increase offset)*/
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("NH: 0x%2X\n", lowpan6_buffer[lowpan6_offset+1]));
IP6H_NEXTH_SET(ip6hdr, lowpan6_buffer[lowpan6_offset++]);
} else {
/* 1: NH compression, LOWPAN_NHC (RFC6282, ch 4.1) */
/* We should fill this later with NHC decoding */
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("NH: skipped, later done with NHC\n"));
IP6H_NEXTH_SET(ip6hdr, 0);
}
/* Set Hop Limit, either carried inline or 3 different hops (1,64,255) */
if ((lowpan6_buffer[0] & 0x03) == 0x00) {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Hops: full value: %d\n", lowpan6_buffer[lowpan6_offset+1]));
IP6H_HOPLIM_SET(ip6hdr, lowpan6_buffer[lowpan6_offset++]);
} else if ((lowpan6_buffer[0] & 0x03) == 0x01) {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Hops: compressed: 1\n"));
IP6H_HOPLIM_SET(ip6hdr, 1);
} else if ((lowpan6_buffer[0] & 0x03) == 0x02) {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Hops: compressed: 64\n"));
IP6H_HOPLIM_SET(ip6hdr, 64);
} else if ((lowpan6_buffer[0] & 0x03) == 0x03) {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Hops: compressed: 255\n"));
IP6H_HOPLIM_SET(ip6hdr, 255);
}
/* Source address decoding. */
if ((lowpan6_buffer[1] & 0x40) == 0x00) {
/* Source address compression (SAC) = 0 -> stateless compression */
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAC == 0, no context byte\n"));
/* Stateless compression */
if ((lowpan6_buffer[1] & 0x30) == 0x00) {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAM == 00, no src compression, fetching 128bits inline\n"));
/* copy full address, increase offset by 16 Bytes */
MEMCPY(&ip6hdr->src.addr[0], lowpan6_buffer + lowpan6_offset, 16);
lowpan6_offset += 16;
} else if ((lowpan6_buffer[1] & 0x30) == 0x10) {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAM == 01, src compression, 64bits inline\n"));
/* set 64 bits to link local */
ip6hdr->src.addr[0] = PP_HTONL(0xfe800000UL);
ip6hdr->src.addr[1] = 0;
/* copy 8 Bytes, increase offset */
MEMCPY(&ip6hdr->src.addr[2], lowpan6_buffer + lowpan6_offset, 8);
lowpan6_offset += 8;
} else if ((lowpan6_buffer[1] & 0x30) == 0x20) {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAM == 10, src compression, 16bits inline\n"));
/* set 96 bits to link local */
ip6hdr->src.addr[0] = PP_HTONL(0xfe800000UL);
ip6hdr->src.addr[1] = 0;
ip6hdr->src.addr[2] = PP_HTONL(0x000000ffUL);
/* extract remaining 16bits from inline bytes, increase offset */
ip6hdr->src.addr[3] = lwip_htonl(0xfe000000UL | (lowpan6_buffer[lowpan6_offset] << 8) |
lowpan6_buffer[lowpan6_offset + 1]);
lowpan6_offset += 2;
} else if ((lowpan6_buffer[1] & 0x30) == 0x30) {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAM == 11, src compression, 0bits inline, using other headers\n"));
/* no information avalaible, using other layers, see RFC6282 ch 3.2.2 */
ip6hdr->src.addr[0] = PP_HTONL(0xfe800000UL);
ip6hdr->src.addr[1] = 0;
if (src->addr_len == 2) {
ip6hdr->src.addr[2] = PP_HTONL(0x000000ffUL);
ip6hdr->src.addr[3] = lwip_htonl(0xfe000000UL | (src->addr[0] << 8) | src->addr[1]);
} else if (src->addr_len == 8) {
ip6hdr->src.addr[2] = lwip_htonl(((src->addr[0] ^ 2) << 24) | (src->addr[1] << 16) |
(src->addr[2] << 8) | src->addr[3]);
ip6hdr->src.addr[3] = lwip_htonl((src->addr[4] << 24) | (src->addr[5] << 16) |
(src->addr[6] << 8) | src->addr[7]);
} else {
/* invalid source address length */
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Invalid source address length\n"));
return ERR_VAL;
}
}
} else {
/* Source address compression (SAC) = 1 -> stateful/context-based compression */
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAC == 1, additional context byte\n"));
if ((lowpan6_buffer[1] & 0x30) == 0x00) {
/* SAM=00, address=> :: (ANY) */
ip6hdr->src.addr[0] = 0;
ip6hdr->src.addr[1] = 0;
ip6hdr->src.addr[2] = 0;
ip6hdr->src.addr[3] = 0;
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAM == 00, context compression, ANY (::)\n"));
} else {
/* Set prefix from context info */
if (lowpan6_buffer[1] & 0x80) {
i = (lowpan6_buffer[2] >> 4) & 0x0f;
} else {
i = 0;
}
if (i >= LWIP_6LOWPAN_NUM_CONTEXTS) {
/* Error */
return ERR_VAL;
}
#if LWIP_6LOWPAN_NUM_CONTEXTS > 0
ip6hdr->src.addr[0] = lowpan6_contexts[i].addr[0];
ip6hdr->src.addr[1] = lowpan6_contexts[i].addr[1];
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAM == xx, context compression found @%d: %8X, %8X\n", (int)i, ip6hdr->src.addr[0], ip6hdr->src.addr[1]));
#else
LWIP_UNUSED_ARG(lowpan6_contexts);
#endif
}
/* determine further address bits */
if ((lowpan6_buffer[1] & 0x30) == 0x10) {
/* SAM=01, load additional 64bits */
MEMCPY(&ip6hdr->src.addr[2], lowpan6_buffer + lowpan6_offset, 8);
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAM == 01, context compression, 64bits inline\n"));
lowpan6_offset += 8;
} else if ((lowpan6_buffer[1] & 0x30) == 0x20) {
/* SAM=01, load additional 16bits */
ip6hdr->src.addr[2] = PP_HTONL(0x000000ffUL);
ip6hdr->src.addr[3] = lwip_htonl(0xfe000000UL | (lowpan6_buffer[lowpan6_offset] << 8) | lowpan6_buffer[lowpan6_offset + 1]);
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAM == 10, context compression, 16bits inline\n"));
lowpan6_offset += 2;
} else if ((lowpan6_buffer[1] & 0x30) == 0x30) {
/* SAM=11, address is fully elided, load from other layers */
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("SAM == 11, context compression, 0bits inline, using other headers\n"));
if (src->addr_len == 2) {
ip6hdr->src.addr[2] = PP_HTONL(0x000000ffUL);
ip6hdr->src.addr[3] = lwip_htonl(0xfe000000UL | (src->addr[0] << 8) | src->addr[1]);
} else if (src->addr_len == 8) {
ip6hdr->src.addr[2] = lwip_htonl(((src->addr[0] ^ 2) << 24) | (src->addr[1] << 16) | (src->addr[2] << 8) | src->addr[3]);
ip6hdr->src.addr[3] = lwip_htonl((src->addr[4] << 24) | (src->addr[5] << 16) | (src->addr[6] << 8) | src->addr[7]);
} else {
/* invalid source address length */
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Invalid source address length\n"));
return ERR_VAL;
}
}
}
/* Destination address decoding. */
if (lowpan6_buffer[1] & 0x08) {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("M=1: multicast\n"));
/* Multicast destination */
if (lowpan6_buffer[1] & 0x04) {
LWIP_DEBUGF(LWIP_DBG_ON,("DAC == 1, context multicast: unsupported!!!\n"));
/* @todo support stateful multicast addressing */
return ERR_VAL;
}
if ((lowpan6_buffer[1] & 0x03) == 0x00) {
/* DAM = 00, copy full address (128bits) */
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("DAM == 00, no dst compression, fetching 128bits inline\n"));
MEMCPY(&ip6hdr->dest.addr[0], lowpan6_buffer + lowpan6_offset, 16);
lowpan6_offset += 16;
} else if ((lowpan6_buffer[1] & 0x03) == 0x01) {
/* DAM = 01, copy 4 bytes (32bits) */
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("DAM == 01, dst address form (48bits): ffXX::00XX:XXXX:XXXX\n"));
ip6hdr->dest.addr[0] = lwip_htonl(0xff000000UL | (lowpan6_buffer[lowpan6_offset++] << 16));
ip6hdr->dest.addr[1] = 0;
ip6hdr->dest.addr[2] = lwip_htonl(lowpan6_buffer[lowpan6_offset++]);
ip6hdr->dest.addr[3] = lwip_htonl((lowpan6_buffer[lowpan6_offset] << 24) | (lowpan6_buffer[lowpan6_offset + 1] << 16) | (lowpan6_buffer[lowpan6_offset + 2] << 8) | lowpan6_buffer[lowpan6_offset + 3]);
lowpan6_offset += 4;
} else if ((lowpan6_buffer[1] & 0x03) == 0x02) {
/* DAM = 10, copy 3 bytes (24bits) */
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("DAM == 10, dst address form (32bits): ffXX::00XX:XXXX\n"));
ip6hdr->dest.addr[0] = lwip_htonl(0xff000000UL | (lowpan6_buffer[lowpan6_offset++] << 16));
ip6hdr->dest.addr[1] = 0;
ip6hdr->dest.addr[2] = 0;
ip6hdr->dest.addr[3] = lwip_htonl((lowpan6_buffer[lowpan6_offset] << 16) | (lowpan6_buffer[lowpan6_offset + 1] << 8) | lowpan6_buffer[lowpan6_offset + 2]);
lowpan6_offset += 3;
} else if ((lowpan6_buffer[1] & 0x03) == 0x03) {
/* DAM = 11, copy 1 byte (8bits) */
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("DAM == 11, dst address form (8bits): ff02::00XX\n"));
ip6hdr->dest.addr[0] = PP_HTONL(0xff020000UL);
ip6hdr->dest.addr[1] = 0;
ip6hdr->dest.addr[2] = 0;
ip6hdr->dest.addr[3] = lwip_htonl(lowpan6_buffer[lowpan6_offset++]);
}
} else {
/* no Multicast (M=0) */
if (lowpan6_buffer[1] & 0x04) {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("DAC == 1, stateful compression\n"));
/* Stateful destination compression */
/* Set prefix from context info */
if (lowpan6_buffer[1] & 0x80) {
i = lowpan6_buffer[2] & 0x0f;
} else {
i = 0;
}
if (i >= LWIP_6LOWPAN_NUM_CONTEXTS) {
/* Error */
return ERR_VAL;
}
#if LWIP_6LOWPAN_NUM_CONTEXTS > 0
ip6hdr->dest.addr[0] = lowpan6_contexts[i].addr[0];
ip6hdr->dest.addr[1] = lowpan6_contexts[i].addr[1];
#endif
} else {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("DAC == 0, stateless compression, setting link local prefix\n"));
/* Link local address compression */
ip6hdr->dest.addr[0] = PP_HTONL(0xfe800000UL);
ip6hdr->dest.addr[1] = 0;
}
/* M=0, DAC=0, determining destination address length via DAM=xx */
if ((lowpan6_buffer[1] & 0x03) == 0x00) {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("DAM == 00, no dst compression, fetching 128bits inline"));
/* DAM=00, copy full address */
MEMCPY(&ip6hdr->dest.addr[0], lowpan6_buffer + lowpan6_offset, 16);
lowpan6_offset += 16;
} else if ((lowpan6_buffer[1] & 0x03) == 0x01) {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("DAM == 01, dst compression, 64bits inline\n"));
/* DAM=01, copy 64 inline bits, increase offset */
MEMCPY(&ip6hdr->dest.addr[2], lowpan6_buffer + lowpan6_offset, 8);
lowpan6_offset += 8;
} else if ((lowpan6_buffer[1] & 0x03) == 0x02) {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("DAM == 01, dst compression, 16bits inline\n"));
/* DAM=10, copy 16 inline bits, increase offset */
ip6hdr->dest.addr[2] = PP_HTONL(0x000000ffUL);
ip6hdr->dest.addr[3] = lwip_htonl(0xfe000000UL | (lowpan6_buffer[lowpan6_offset] << 8) | lowpan6_buffer[lowpan6_offset + 1]);
lowpan6_offset += 2;
} else if ((lowpan6_buffer[1] & 0x03) == 0x03) {
/* DAM=11, no bits available, use other headers (not done here) */
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG,("DAM == 01, dst compression, 0bits inline, using other headers\n"));
if (dest->addr_len == 2) {
ip6hdr->dest.addr[2] = PP_HTONL(0x000000ffUL);
ip6hdr->dest.addr[3] = lwip_htonl(0xfe000000UL | (dest->addr[0] << 8) | dest->addr[1]);
} else if (dest->addr_len == 8) {
ip6hdr->dest.addr[2] = lwip_htonl(((dest->addr[0] ^ 2) << 24) | (dest->addr[1] << 16) | dest->addr[2] << 8 | dest->addr[3]);
ip6hdr->dest.addr[3] = lwip_htonl((dest->addr[4] << 24) | (dest->addr[5] << 16) | dest->addr[6] << 8 | dest->addr[7]);
} else {
/* invalid destination address length */
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("Invalid destination address length\n"));
return ERR_VAL;
}
}
}
/* Next Header Compression (NHC) decoding? */
if (lowpan6_buffer[0] & 0x04) {
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("NHC decoding\n"));
#if LWIP_UDP
if ((lowpan6_buffer[lowpan6_offset] & 0xf8) == 0xf0) {
/* NHC: UDP */
struct udp_hdr *udphdr;
LWIP_DEBUGF(LWIP_LOWPAN6_DECOMPRESSION_DEBUG, ("NHC: UDP\n"));
/* UDP compression */
IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_UDP);
udphdr = (struct udp_hdr *)((u8_t *)decomp_buffer + ip6_offset);
if (decomp_bufsize < IP6_HLEN + UDP_HLEN) {
return ERR_MEM;
}
/* Checksum decompression */
if (lowpan6_buffer[lowpan6_offset] & 0x04) {
/* @todo support checksum decompress */
LWIP_DEBUGF(LWIP_DBG_ON, ("NHC: UDP chechsum decompression UNSUPPORTED\n"));
return ERR_VAL;
}
/* Decompress ports, according to RFC4944 */
i = lowpan6_buffer[lowpan6_offset++] & 0x03;
if (i == 0) {
udphdr->src = lwip_htons(lowpan6_buffer[lowpan6_offset] << 8 | lowpan6_buffer[lowpan6_offset + 1]);
udphdr->dest = lwip_htons(lowpan6_buffer[lowpan6_offset + 2] << 8 | lowpan6_buffer[lowpan6_offset + 3]);
lowpan6_offset += 4;
} else if (i == 0x01) {
udphdr->src = lwip_htons(lowpan6_buffer[lowpan6_offset] << 8 | lowpan6_buffer[lowpan6_offset + 1]);
udphdr->dest = lwip_htons(0xf000 | lowpan6_buffer[lowpan6_offset + 2]);
lowpan6_offset += 3;
} else if (i == 0x02) {
udphdr->src = lwip_htons(0xf000 | lowpan6_buffer[lowpan6_offset]);
udphdr->dest = lwip_htons(lowpan6_buffer[lowpan6_offset + 1] << 8 | lowpan6_buffer[lowpan6_offset + 2]);
lowpan6_offset += 3;
} else if (i == 0x03) {
udphdr->src = lwip_htons(0xf0b0 | ((lowpan6_buffer[lowpan6_offset] >> 4) & 0x0f));
udphdr->dest = lwip_htons(0xf0b0 | (lowpan6_buffer[lowpan6_offset] & 0x0f));
lowpan6_offset += 1;
}
udphdr->chksum = lwip_htons(lowpan6_buffer[lowpan6_offset] << 8 | lowpan6_buffer[lowpan6_offset + 1]);
lowpan6_offset += 2;
ip6_offset += UDP_HLEN;
if (datagram_size == 0) {
datagram_size = compressed_size - lowpan6_offset + ip6_offset;
}
udphdr->len = lwip_htons(datagram_size - IP6_HLEN);
} else
#endif /* LWIP_UDP */
{
LWIP_DEBUGF(LWIP_DBG_ON,("NHC: unsupported protocol!\n"));
/* @todo support NHC other than UDP */
return ERR_VAL;
}
}
if (datagram_size == 0) {
datagram_size = compressed_size - lowpan6_offset + ip6_offset;
}
/* Infer IPv6 payload length for header */
IP6H_PLEN_SET(ip6hdr, datagram_size - IP6_HLEN);
if (lowpan6_offset > lowpan6_bufsize) {
/* input buffer overflow */
return ERR_VAL;
}
*hdr_size_comp = lowpan6_offset;
*hdr_size_decomp = ip6_offset;
return ERR_OK;
}
struct pbuf *
lowpan6_decompress(struct pbuf *p, u16_t datagram_size, ip6_addr_t *lowpan6_contexts,
struct lowpan6_link_addr *src, struct lowpan6_link_addr *dest)
{
struct pbuf *q;
u16_t lowpan6_offset, ip6_offset;
err_t err;
#if LWIP_UDP
#define UDP_HLEN_ALLOC UDP_HLEN
#else
#define UDP_HLEN_ALLOC 0
#endif
/* Allocate a buffer for decompression. This buffer will be too big and will be
trimmed once the final size is known. */
q = pbuf_alloc(PBUF_IP, p->len + IP6_HLEN + UDP_HLEN_ALLOC, PBUF_POOL);
if (q == NULL) {
pbuf_free(p);
return NULL;
}
if (q->len < IP6_HLEN + UDP_HLEN_ALLOC) {
/* The headers need to fit into the first pbuf */
pbuf_free(p);
pbuf_free(q);
return NULL;
}
/* Decompress the IPv6 (and possibly UDP) header(s) into the new pbuf */
err = lowpan6_decompress_hdr((u8_t *)p->payload, p->len, (u8_t *)q->payload, q->len,
&lowpan6_offset, &ip6_offset, datagram_size, p->tot_len, lowpan6_contexts, src, dest);
if (err != ERR_OK) {
pbuf_free(p);
pbuf_free(q);
return NULL;
}
/* Now we copy leftover contents from p to q, so we have all L2 and L3 headers
(and L4?) in a single pbuf: */
/* Hide the compressed headers in p */
pbuf_remove_header(p, lowpan6_offset);
/* Temporarily hide the headers in q... */
pbuf_remove_header(q, ip6_offset);
/* ... copy the rest of p into q... */
pbuf_copy(q, p);
/* ... and reveal the headers again... */
pbuf_add_header_force(q, ip6_offset);
/* ... trim the pbuf to its correct size... */
pbuf_realloc(q, ip6_offset + p->len);
/* ... and cat possibly remaining (data-only) pbufs */
if (p->next != NULL) {
pbuf_cat(q, p->next);
}
/* the original (first) pbuf can now be freed */
p->next = NULL;
pbuf_free(p);
/* all done */
return q;
}
#endif /* LWIP_6LOWPAN_IPHC */
#endif /* LWIP_IPV6 */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,126 @@
/*
* chap-md5.c - New CHAP/MD5 implementation.
*
* Copyright (c) 2003 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to
* endorse or promote products derived from this software without
* prior written permission.
*
* 3. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Paul Mackerras
* <paulus@samba.org>".
*
* THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT && CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
#if 0 /* UNUSED */
#include <stdlib.h>
#include <string.h>
#endif /* UNUSED */
#include "netif/ppp/ppp_impl.h"
#include "netif/ppp/chap-new.h"
#include "netif/ppp/chap-md5.h"
#include "netif/ppp/magic.h"
#include "netif/ppp/pppcrypt.h"
#define MD5_HASH_SIZE 16
#define MD5_MIN_CHALLENGE 17
#define MD5_MAX_CHALLENGE 24
#define MD5_MIN_MAX_POWER_OF_TWO_CHALLENGE 3 /* 2^3-1 = 7, 17+7 = 24 */
#if PPP_SERVER
static void chap_md5_generate_challenge(ppp_pcb *pcb, unsigned char *cp) {
int clen;
LWIP_UNUSED_ARG(pcb);
clen = MD5_MIN_CHALLENGE + magic_pow(MD5_MIN_MAX_POWER_OF_TWO_CHALLENGE);
*cp++ = clen;
magic_random_bytes(cp, clen);
}
static int chap_md5_verify_response(ppp_pcb *pcb, int id, const char *name,
const unsigned char *secret, int secret_len,
const unsigned char *challenge, const unsigned char *response,
char *message, int message_space) {
lwip_md5_context ctx;
unsigned char idbyte = id;
unsigned char hash[MD5_HASH_SIZE];
int challenge_len, response_len;
LWIP_UNUSED_ARG(name);
LWIP_UNUSED_ARG(pcb);
challenge_len = *challenge++;
response_len = *response++;
if (response_len == MD5_HASH_SIZE) {
/* Generate hash of ID, secret, challenge */
lwip_md5_init(&ctx);
lwip_md5_starts(&ctx);
lwip_md5_update(&ctx, &idbyte, 1);
lwip_md5_update(&ctx, secret, secret_len);
lwip_md5_update(&ctx, challenge, challenge_len);
lwip_md5_finish(&ctx, hash);
lwip_md5_free(&ctx);
/* Test if our hash matches the peer's response */
if (memcmp(hash, response, MD5_HASH_SIZE) == 0) {
ppp_slprintf(message, message_space, "Access granted");
return 1;
}
}
ppp_slprintf(message, message_space, "Access denied");
return 0;
}
#endif /* PPP_SERVER */
static void chap_md5_make_response(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name,
const unsigned char *challenge, const char *secret, int secret_len,
unsigned char *private_) {
lwip_md5_context ctx;
unsigned char idbyte = id;
int challenge_len = *challenge++;
LWIP_UNUSED_ARG(our_name);
LWIP_UNUSED_ARG(private_);
LWIP_UNUSED_ARG(pcb);
lwip_md5_init(&ctx);
lwip_md5_starts(&ctx);
lwip_md5_update(&ctx, &idbyte, 1);
lwip_md5_update(&ctx, (const u_char *)secret, secret_len);
lwip_md5_update(&ctx, challenge, challenge_len);
lwip_md5_finish(&ctx, &response[1]);
lwip_md5_free(&ctx);
response[0] = MD5_HASH_SIZE;
}
const struct chap_digest_type md5_digest = {
CHAP_MD5, /* code */
#if PPP_SERVER
chap_md5_generate_challenge,
chap_md5_verify_response,
#endif /* PPP_SERVER */
chap_md5_make_response,
NULL, /* check_success */
NULL, /* handle_failure */
};
#endif /* PPP_SUPPORT && CHAP_SUPPORT */

View File

@@ -0,0 +1,677 @@
/*
* chap-new.c - New CHAP implementation.
*
* Copyright (c) 2003 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to
* endorse or promote products derived from this software without
* prior written permission.
*
* 3. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Paul Mackerras
* <paulus@samba.org>".
*
* THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT && CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
#if 0 /* UNUSED */
#include <stdlib.h>
#include <string.h>
#endif /* UNUSED */
#include "netif/ppp/ppp_impl.h"
#if 0 /* UNUSED */
#include "session.h"
#endif /* UNUSED */
#include "netif/ppp/chap-new.h"
#include "netif/ppp/chap-md5.h"
#if MSCHAP_SUPPORT
#include "netif/ppp/chap_ms.h"
#endif
#include "netif/ppp/magic.h"
#if 0 /* UNUSED */
/* Hook for a plugin to validate CHAP challenge */
int (*chap_verify_hook)(const char *name, const char *ourname, int id,
const struct chap_digest_type *digest,
const unsigned char *challenge, const unsigned char *response,
char *message, int message_space) = NULL;
#endif /* UNUSED */
#if PPP_OPTIONS
/*
* Command-line options.
*/
static option_t chap_option_list[] = {
{ "chap-restart", o_int, &chap_timeout_time,
"Set timeout for CHAP", OPT_PRIO },
{ "chap-max-challenge", o_int, &pcb->settings.chap_max_transmits,
"Set max #xmits for challenge", OPT_PRIO },
{ "chap-interval", o_int, &pcb->settings.chap_rechallenge_time,
"Set interval for rechallenge", OPT_PRIO },
{ NULL }
};
#endif /* PPP_OPTIONS */
/* Values for flags in chap_client_state and chap_server_state */
#define LOWERUP 1
#define AUTH_STARTED 2
#define AUTH_DONE 4
#define AUTH_FAILED 8
#define TIMEOUT_PENDING 0x10
#define CHALLENGE_VALID 0x20
/*
* Prototypes.
*/
static void chap_init(ppp_pcb *pcb);
static void chap_lowerup(ppp_pcb *pcb);
static void chap_lowerdown(ppp_pcb *pcb);
#if PPP_SERVER
static void chap_timeout(void *arg);
static void chap_generate_challenge(ppp_pcb *pcb);
static void chap_handle_response(ppp_pcb *pcb, int code,
unsigned char *pkt, int len);
static int chap_verify_response(ppp_pcb *pcb, const char *name, const char *ourname, int id,
const struct chap_digest_type *digest,
const unsigned char *challenge, const unsigned char *response,
char *message, int message_space);
#endif /* PPP_SERVER */
static void chap_respond(ppp_pcb *pcb, int id,
unsigned char *pkt, int len);
static void chap_handle_status(ppp_pcb *pcb, int code, int id,
unsigned char *pkt, int len);
static void chap_protrej(ppp_pcb *pcb);
static void chap_input(ppp_pcb *pcb, unsigned char *pkt, int pktlen);
#if PRINTPKT_SUPPORT
static int chap_print_pkt(const unsigned char *p, int plen,
void (*printer) (void *, const char *, ...), void *arg);
#endif /* PRINTPKT_SUPPORT */
/* List of digest types that we know about */
static const struct chap_digest_type* const chap_digests[] = {
&md5_digest,
#if MSCHAP_SUPPORT
&chapms_digest,
&chapms2_digest,
#endif /* MSCHAP_SUPPORT */
NULL
};
/*
* chap_init - reset to initial state.
*/
static void chap_init(ppp_pcb *pcb) {
LWIP_UNUSED_ARG(pcb);
#if 0 /* Not necessary, everything is cleared in ppp_new() */
memset(&pcb->chap_client, 0, sizeof(chap_client_state));
#if PPP_SERVER
memset(&pcb->chap_server, 0, sizeof(chap_server_state));
#endif /* PPP_SERVER */
#endif /* 0 */
}
/*
* chap_lowerup - we can start doing stuff now.
*/
static void chap_lowerup(ppp_pcb *pcb) {
pcb->chap_client.flags |= LOWERUP;
#if PPP_SERVER
pcb->chap_server.flags |= LOWERUP;
if (pcb->chap_server.flags & AUTH_STARTED)
chap_timeout(pcb);
#endif /* PPP_SERVER */
}
static void chap_lowerdown(ppp_pcb *pcb) {
pcb->chap_client.flags = 0;
#if PPP_SERVER
if (pcb->chap_server.flags & TIMEOUT_PENDING)
UNTIMEOUT(chap_timeout, pcb);
pcb->chap_server.flags = 0;
#endif /* PPP_SERVER */
}
#if PPP_SERVER
/*
* chap_auth_peer - Start authenticating the peer.
* If the lower layer is already up, we start sending challenges,
* otherwise we wait for the lower layer to come up.
*/
void chap_auth_peer(ppp_pcb *pcb, const char *our_name, int digest_code) {
const struct chap_digest_type *dp;
int i;
if (pcb->chap_server.flags & AUTH_STARTED) {
ppp_error("CHAP: peer authentication already started!");
return;
}
for (i = 0; (dp = chap_digests[i]) != NULL; ++i)
if (dp->code == digest_code)
break;
if (dp == NULL)
ppp_fatal("CHAP digest 0x%x requested but not available",
digest_code);
pcb->chap_server.digest = dp;
pcb->chap_server.name = our_name;
/* Start with a random ID value */
pcb->chap_server.id = magic();
pcb->chap_server.flags |= AUTH_STARTED;
if (pcb->chap_server.flags & LOWERUP)
chap_timeout(pcb);
}
#endif /* PPP_SERVER */
/*
* chap_auth_with_peer - Prepare to authenticate ourselves to the peer.
* There isn't much to do until we receive a challenge.
*/
void chap_auth_with_peer(ppp_pcb *pcb, const char *our_name, int digest_code) {
const struct chap_digest_type *dp;
int i;
if(NULL == our_name)
return;
if (pcb->chap_client.flags & AUTH_STARTED) {
ppp_error("CHAP: authentication with peer already started!");
return;
}
for (i = 0; (dp = chap_digests[i]) != NULL; ++i)
if (dp->code == digest_code)
break;
if (dp == NULL)
ppp_fatal("CHAP digest 0x%x requested but not available",
digest_code);
pcb->chap_client.digest = dp;
pcb->chap_client.name = our_name;
pcb->chap_client.flags |= AUTH_STARTED;
}
#if PPP_SERVER
/*
* chap_timeout - It's time to send another challenge to the peer.
* This could be either a retransmission of a previous challenge,
* or a new challenge to start re-authentication.
*/
static void chap_timeout(void *arg) {
ppp_pcb *pcb = (ppp_pcb*)arg;
struct pbuf *p;
pcb->chap_server.flags &= ~TIMEOUT_PENDING;
if ((pcb->chap_server.flags & CHALLENGE_VALID) == 0) {
pcb->chap_server.challenge_xmits = 0;
chap_generate_challenge(pcb);
pcb->chap_server.flags |= CHALLENGE_VALID;
} else if (pcb->chap_server.challenge_xmits >= pcb->settings.chap_max_transmits) {
pcb->chap_server.flags &= ~CHALLENGE_VALID;
pcb->chap_server.flags |= AUTH_DONE | AUTH_FAILED;
auth_peer_fail(pcb, PPP_CHAP);
return;
}
p = pbuf_alloc(PBUF_RAW, (u16_t)(pcb->chap_server.challenge_pktlen), PPP_CTRL_PBUF_TYPE);
if(NULL == p)
return;
if(p->tot_len != p->len) {
pbuf_free(p);
return;
}
MEMCPY(p->payload, pcb->chap_server.challenge, pcb->chap_server.challenge_pktlen);
ppp_write(pcb, p);
++pcb->chap_server.challenge_xmits;
pcb->chap_server.flags |= TIMEOUT_PENDING;
TIMEOUT(chap_timeout, arg, pcb->settings.chap_timeout_time);
}
/*
* chap_generate_challenge - generate a challenge string and format
* the challenge packet in pcb->chap_server.challenge_pkt.
*/
static void chap_generate_challenge(ppp_pcb *pcb) {
int clen = 1, nlen, len;
unsigned char *p;
p = pcb->chap_server.challenge;
MAKEHEADER(p, PPP_CHAP);
p += CHAP_HDRLEN;
pcb->chap_server.digest->generate_challenge(pcb, p);
clen = *p;
nlen = strlen(pcb->chap_server.name);
memcpy(p + 1 + clen, pcb->chap_server.name, nlen);
len = CHAP_HDRLEN + 1 + clen + nlen;
pcb->chap_server.challenge_pktlen = PPP_HDRLEN + len;
p = pcb->chap_server.challenge + PPP_HDRLEN;
p[0] = CHAP_CHALLENGE;
p[1] = ++pcb->chap_server.id;
p[2] = len >> 8;
p[3] = len;
}
/*
* chap_handle_response - check the response to our challenge.
*/
static void chap_handle_response(ppp_pcb *pcb, int id,
unsigned char *pkt, int len) {
int response_len, ok, mlen;
const unsigned char *response;
unsigned char *outp;
struct pbuf *p;
const char *name = NULL; /* initialized to shut gcc up */
#if 0 /* UNUSED */
int (*verifier)(const char *, const char *, int, const struct chap_digest_type *,
const unsigned char *, const unsigned char *, char *, int);
#endif /* UNUSED */
char rname[MAXNAMELEN+1];
char message[256];
if ((pcb->chap_server.flags & LOWERUP) == 0)
return;
if (id != pcb->chap_server.challenge[PPP_HDRLEN+1] || len < 2)
return;
if (pcb->chap_server.flags & CHALLENGE_VALID) {
response = pkt;
GETCHAR(response_len, pkt);
len -= response_len + 1; /* length of name */
name = (char *)pkt + response_len;
if (len < 0)
return;
if (pcb->chap_server.flags & TIMEOUT_PENDING) {
pcb->chap_server.flags &= ~TIMEOUT_PENDING;
UNTIMEOUT(chap_timeout, pcb);
}
#if PPP_REMOTENAME
if (pcb->settings.explicit_remote) {
name = pcb->remote_name;
} else
#endif /* PPP_REMOTENAME */
{
/* Null terminate and clean remote name. */
ppp_slprintf(rname, sizeof(rname), "%.*v", len, name);
name = rname;
}
#if 0 /* UNUSED */
if (chap_verify_hook)
verifier = chap_verify_hook;
else
verifier = chap_verify_response;
ok = (*verifier)(name, pcb->chap_server.name, id, pcb->chap_server.digest,
pcb->chap_server.challenge + PPP_HDRLEN + CHAP_HDRLEN,
response, pcb->chap_server.message, sizeof(pcb->chap_server.message));
#endif /* UNUSED */
ok = chap_verify_response(pcb, name, pcb->chap_server.name, id, pcb->chap_server.digest,
pcb->chap_server.challenge + PPP_HDRLEN + CHAP_HDRLEN,
response, message, sizeof(message));
#if 0 /* UNUSED */
if (!ok || !auth_number()) {
#endif /* UNUSED */
if (!ok) {
pcb->chap_server.flags |= AUTH_FAILED;
ppp_warn("Peer %q failed CHAP authentication", name);
}
} else if ((pcb->chap_server.flags & AUTH_DONE) == 0)
return;
/* send the response */
mlen = strlen(message);
len = CHAP_HDRLEN + mlen;
p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN +len), PPP_CTRL_PBUF_TYPE);
if(NULL == p)
return;
if(p->tot_len != p->len) {
pbuf_free(p);
return;
}
outp = (unsigned char *)p->payload;
MAKEHEADER(outp, PPP_CHAP);
outp[0] = (pcb->chap_server.flags & AUTH_FAILED)? CHAP_FAILURE: CHAP_SUCCESS;
outp[1] = id;
outp[2] = len >> 8;
outp[3] = len;
if (mlen > 0)
memcpy(outp + CHAP_HDRLEN, message, mlen);
ppp_write(pcb, p);
if (pcb->chap_server.flags & CHALLENGE_VALID) {
pcb->chap_server.flags &= ~CHALLENGE_VALID;
if (!(pcb->chap_server.flags & AUTH_DONE) && !(pcb->chap_server.flags & AUTH_FAILED)) {
#if 0 /* UNUSED */
/*
* Auth is OK, so now we need to check session restrictions
* to ensure everything is OK, but only if we used a
* plugin, and only if we're configured to check. This
* allows us to do PAM checks on PPP servers that
* authenticate against ActiveDirectory, and use AD for
* account info (like when using Winbind integrated with
* PAM).
*/
if (session_mgmt &&
session_check(name, NULL, devnam, NULL) == 0) {
pcb->chap_server.flags |= AUTH_FAILED;
ppp_warn("Peer %q failed CHAP Session verification", name);
}
#endif /* UNUSED */
}
if (pcb->chap_server.flags & AUTH_FAILED) {
auth_peer_fail(pcb, PPP_CHAP);
} else {
if ((pcb->chap_server.flags & AUTH_DONE) == 0)
auth_peer_success(pcb, PPP_CHAP,
pcb->chap_server.digest->code,
name, strlen(name));
if (pcb->settings.chap_rechallenge_time) {
pcb->chap_server.flags |= TIMEOUT_PENDING;
TIMEOUT(chap_timeout, pcb,
pcb->settings.chap_rechallenge_time);
}
}
pcb->chap_server.flags |= AUTH_DONE;
}
}
/*
* chap_verify_response - check whether the peer's response matches
* what we think it should be. Returns 1 if it does (authentication
* succeeded), or 0 if it doesn't.
*/
static int chap_verify_response(ppp_pcb *pcb, const char *name, const char *ourname, int id,
const struct chap_digest_type *digest,
const unsigned char *challenge, const unsigned char *response,
char *message, int message_space) {
int ok;
unsigned char secret[MAXSECRETLEN];
int secret_len;
/* Get the secret that the peer is supposed to know */
if (!get_secret(pcb, name, ourname, (char *)secret, &secret_len, 1)) {
ppp_error("No CHAP secret found for authenticating %q", name);
return 0;
}
ok = digest->verify_response(pcb, id, name, secret, secret_len, challenge,
response, message, message_space);
memset(secret, 0, sizeof(secret));
return ok;
}
#endif /* PPP_SERVER */
/*
* chap_respond - Generate and send a response to a challenge.
*/
static void chap_respond(ppp_pcb *pcb, int id,
unsigned char *pkt, int len) {
int clen, nlen;
int secret_len;
struct pbuf *p;
u_char *outp;
char rname[MAXNAMELEN+1];
char secret[MAXSECRETLEN+1];
p = pbuf_alloc(PBUF_RAW, (u16_t)(RESP_MAX_PKTLEN), PPP_CTRL_PBUF_TYPE);
if(NULL == p)
return;
if(p->tot_len != p->len) {
pbuf_free(p);
return;
}
if ((pcb->chap_client.flags & (LOWERUP | AUTH_STARTED)) != (LOWERUP | AUTH_STARTED))
return; /* not ready */
if (len < 2 || len < pkt[0] + 1)
return; /* too short */
clen = pkt[0];
nlen = len - (clen + 1);
/* Null terminate and clean remote name. */
ppp_slprintf(rname, sizeof(rname), "%.*v", nlen, pkt + clen + 1);
#if PPP_REMOTENAME
/* Microsoft doesn't send their name back in the PPP packet */
if (pcb->settings.explicit_remote || (pcb->settings.remote_name[0] != 0 && rname[0] == 0))
strlcpy(rname, pcb->settings.remote_name, sizeof(rname));
#endif /* PPP_REMOTENAME */
/* get secret for authenticating ourselves with the specified host */
if (!get_secret(pcb, pcb->chap_client.name, rname, secret, &secret_len, 0)) {
secret_len = 0; /* assume null secret if can't find one */
ppp_warn("No CHAP secret found for authenticating us to %q", rname);
}
outp = (u_char*)p->payload;
MAKEHEADER(outp, PPP_CHAP);
outp += CHAP_HDRLEN;
pcb->chap_client.digest->make_response(pcb, outp, id, pcb->chap_client.name, pkt,
secret, secret_len, pcb->chap_client.priv);
memset(secret, 0, secret_len);
clen = *outp;
nlen = strlen(pcb->chap_client.name);
memcpy(outp + clen + 1, pcb->chap_client.name, nlen);
outp = (u_char*)p->payload + PPP_HDRLEN;
len = CHAP_HDRLEN + clen + 1 + nlen;
outp[0] = CHAP_RESPONSE;
outp[1] = id;
outp[2] = len >> 8;
outp[3] = len;
pbuf_realloc(p, PPP_HDRLEN + len);
ppp_write(pcb, p);
}
static void chap_handle_status(ppp_pcb *pcb, int code, int id,
unsigned char *pkt, int len) {
const char *msg = NULL;
LWIP_UNUSED_ARG(id);
if ((pcb->chap_client.flags & (AUTH_DONE|AUTH_STARTED|LOWERUP))
!= (AUTH_STARTED|LOWERUP))
return;
pcb->chap_client.flags |= AUTH_DONE;
if (code == CHAP_SUCCESS) {
/* used for MS-CHAP v2 mutual auth, yuck */
if (pcb->chap_client.digest->check_success != NULL) {
if (!(*pcb->chap_client.digest->check_success)(pcb, pkt, len, pcb->chap_client.priv))
code = CHAP_FAILURE;
} else
msg = "CHAP authentication succeeded";
} else {
if (pcb->chap_client.digest->handle_failure != NULL)
(*pcb->chap_client.digest->handle_failure)(pcb, pkt, len);
else
msg = "CHAP authentication failed";
}
if (msg) {
if (len > 0)
ppp_info("%s: %.*v", msg, len, pkt);
else
ppp_info("%s", msg);
}
if (code == CHAP_SUCCESS)
auth_withpeer_success(pcb, PPP_CHAP, pcb->chap_client.digest->code);
else {
pcb->chap_client.flags |= AUTH_FAILED;
ppp_error("CHAP authentication failed");
auth_withpeer_fail(pcb, PPP_CHAP);
}
}
static void chap_input(ppp_pcb *pcb, unsigned char *pkt, int pktlen) {
unsigned char code, id;
int len;
if (pktlen < CHAP_HDRLEN)
return;
GETCHAR(code, pkt);
GETCHAR(id, pkt);
GETSHORT(len, pkt);
if (len < CHAP_HDRLEN || len > pktlen)
return;
len -= CHAP_HDRLEN;
switch (code) {
case CHAP_CHALLENGE:
chap_respond(pcb, id, pkt, len);
break;
#if PPP_SERVER
case CHAP_RESPONSE:
chap_handle_response(pcb, id, pkt, len);
break;
#endif /* PPP_SERVER */
case CHAP_FAILURE:
case CHAP_SUCCESS:
chap_handle_status(pcb, code, id, pkt, len);
break;
default:
break;
}
}
static void chap_protrej(ppp_pcb *pcb) {
#if PPP_SERVER
if (pcb->chap_server.flags & TIMEOUT_PENDING) {
pcb->chap_server.flags &= ~TIMEOUT_PENDING;
UNTIMEOUT(chap_timeout, pcb);
}
if (pcb->chap_server.flags & AUTH_STARTED) {
pcb->chap_server.flags = 0;
auth_peer_fail(pcb, PPP_CHAP);
}
#endif /* PPP_SERVER */
if ((pcb->chap_client.flags & (AUTH_STARTED|AUTH_DONE)) == AUTH_STARTED) {
pcb->chap_client.flags &= ~AUTH_STARTED;
ppp_error("CHAP authentication failed due to protocol-reject");
auth_withpeer_fail(pcb, PPP_CHAP);
}
}
#if PRINTPKT_SUPPORT
/*
* chap_print_pkt - print the contents of a CHAP packet.
*/
static const char* const chap_code_names[] = {
"Challenge", "Response", "Success", "Failure"
};
static int chap_print_pkt(const unsigned char *p, int plen,
void (*printer) (void *, const char *, ...), void *arg) {
int code, id, len;
int clen, nlen;
unsigned char x;
if (plen < CHAP_HDRLEN)
return 0;
GETCHAR(code, p);
GETCHAR(id, p);
GETSHORT(len, p);
if (len < CHAP_HDRLEN || len > plen)
return 0;
if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(chap_code_names))
printer(arg, " %s", chap_code_names[code-1]);
else
printer(arg, " code=0x%x", code);
printer(arg, " id=0x%x", id);
len -= CHAP_HDRLEN;
switch (code) {
case CHAP_CHALLENGE:
case CHAP_RESPONSE:
if (len < 1)
break;
clen = p[0];
if (len < clen + 1)
break;
++p;
nlen = len - clen - 1;
printer(arg, " <");
for (; clen > 0; --clen) {
GETCHAR(x, p);
printer(arg, "%.2x", x);
}
printer(arg, ">, name = ");
ppp_print_string(p, nlen, printer, arg);
break;
case CHAP_FAILURE:
case CHAP_SUCCESS:
printer(arg, " ");
ppp_print_string(p, len, printer, arg);
break;
default:
for (clen = len; clen > 0; --clen) {
GETCHAR(x, p);
printer(arg, " %.2x", x);
}
/* no break */
}
return len + CHAP_HDRLEN;
}
#endif /* PRINTPKT_SUPPORT */
const struct protent chap_protent = {
PPP_CHAP,
chap_init,
chap_input,
chap_protrej,
chap_lowerup,
chap_lowerdown,
NULL, /* open */
NULL, /* close */
#if PRINTPKT_SUPPORT
chap_print_pkt,
#endif /* PRINTPKT_SUPPORT */
#if PPP_DATAINPUT
NULL, /* datainput */
#endif /* PPP_DATAINPUT */
#if PRINTPKT_SUPPORT
"CHAP", /* name */
NULL, /* data_name */
#endif /* PRINTPKT_SUPPORT */
#if PPP_OPTIONS
chap_option_list,
NULL, /* check_options */
#endif /* PPP_OPTIONS */
#if DEMAND_SUPPORT
NULL,
NULL
#endif /* DEMAND_SUPPORT */
};
#endif /* PPP_SUPPORT && CHAP_SUPPORT */

View File

@@ -0,0 +1,962 @@
/*
* chap_ms.c - Microsoft MS-CHAP compatible implementation.
*
* Copyright (c) 1995 Eric Rosenquist. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name(s) of the authors of this software must not be used to
* endorse or promote products derived from this software without
* prior written permission.
*
* THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
*
* Implemented LANManager type password response to MS-CHAP challenges.
* Now pppd provides both NT style and LANMan style blocks, and the
* prefered is set by option "ms-lanman". Default is to use NT.
* The hash text (StdText) was taken from Win95 RASAPI32.DLL.
*
* You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
*/
/*
* Modifications by Frank Cusack, frank@google.com, March 2002.
*
* Implemented MS-CHAPv2 functionality, heavily based on sample
* implementation in RFC 2759. Implemented MPPE functionality,
* heavily based on sample implementation in RFC 3079.
*
* Copyright (c) 2002 Google, Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name(s) of the authors of this software must not be used to
* endorse or promote products derived from this software without
* prior written permission.
*
* THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT && MSCHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
#if 0 /* UNUSED */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#endif /* UNUSED */
#include "netif/ppp/ppp_impl.h"
#include "netif/ppp/chap-new.h"
#include "netif/ppp/chap_ms.h"
#include "netif/ppp/pppcrypt.h"
#include "netif/ppp/magic.h"
#if MPPE_SUPPORT
#include "netif/ppp/mppe.h" /* For mppe_sha1_pad*, mppe_set_key() */
#endif /* MPPE_SUPPORT */
#define SHA1_SIGNATURE_SIZE 20
#define MD4_SIGNATURE_SIZE 16 /* 16 bytes in a MD4 message digest */
#define MAX_NT_PASSWORD 256 /* Max (Unicode) chars in an NT pass */
#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */
#define MS_CHAP2_RESPONSE_LEN 49 /* Response length for MS-CHAPv2 */
#define MS_AUTH_RESPONSE_LENGTH 40 /* MS-CHAPv2 authenticator response, */
/* as ASCII */
/* Error codes for MS-CHAP failure messages. */
#define MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS 646
#define MS_CHAP_ERROR_ACCT_DISABLED 647
#define MS_CHAP_ERROR_PASSWD_EXPIRED 648
#define MS_CHAP_ERROR_NO_DIALIN_PERMISSION 649
#define MS_CHAP_ERROR_AUTHENTICATION_FAILURE 691
#define MS_CHAP_ERROR_CHANGING_PASSWORD 709
/*
* Offsets within the response field for MS-CHAP
*/
#define MS_CHAP_LANMANRESP 0
#define MS_CHAP_LANMANRESP_LEN 24
#define MS_CHAP_NTRESP 24
#define MS_CHAP_NTRESP_LEN 24
#define MS_CHAP_USENT 48
/*
* Offsets within the response field for MS-CHAP2
*/
#define MS_CHAP2_PEER_CHALLENGE 0
#define MS_CHAP2_PEER_CHAL_LEN 16
#define MS_CHAP2_RESERVED_LEN 8
#define MS_CHAP2_NTRESP 24
#define MS_CHAP2_NTRESP_LEN 24
#define MS_CHAP2_FLAGS 48
#if MPPE_SUPPORT
#if 0 /* UNUSED */
/* These values are the RADIUS attribute values--see RFC 2548. */
#define MPPE_ENC_POL_ENC_ALLOWED 1
#define MPPE_ENC_POL_ENC_REQUIRED 2
#define MPPE_ENC_TYPES_RC4_40 2
#define MPPE_ENC_TYPES_RC4_128 4
/* used by plugins (using above values) */
extern void set_mppe_enc_types(int, int);
#endif /* UNUSED */
#endif /* MPPE_SUPPORT */
/* Are we the authenticator or authenticatee? For MS-CHAPv2 key derivation. */
#define MS_CHAP2_AUTHENTICATEE 0
#define MS_CHAP2_AUTHENTICATOR 1
static void ascii2unicode (const char[], int, u_char[]);
static void NTPasswordHash (u_char *, int, u_char[MD4_SIGNATURE_SIZE]);
static void ChallengeResponse (const u_char *, const u_char *, u_char[24]);
static void ChallengeHash (const u_char[16], const u_char *, const char *, u_char[8]);
static void ChapMS_NT (const u_char *, const char *, int, u_char[24]);
static void ChapMS2_NT (const u_char *, const u_char[16], const char *, const char *, int,
u_char[24]);
static void GenerateAuthenticatorResponsePlain
(const char*, int, u_char[24], const u_char[16], const u_char *,
const char *, u_char[41]);
#ifdef MSLANMAN
static void ChapMS_LANMan (u_char *, char *, int, u_char *);
#endif
static void GenerateAuthenticatorResponse(const u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
u_char NTResponse[24], const u_char PeerChallenge[16],
const u_char *rchallenge, const char *username,
u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]);
#if MPPE_SUPPORT
static void Set_Start_Key (ppp_pcb *pcb, const u_char *, const char *, int);
static void SetMasterKeys (ppp_pcb *pcb, const char *, int, u_char[24], int);
#endif /* MPPE_SUPPORT */
static void ChapMS (ppp_pcb *pcb, const u_char *, const char *, int, u_char *);
static void ChapMS2 (ppp_pcb *pcb, const u_char *, const u_char *, const char *, const char *, int,
u_char *, u_char[MS_AUTH_RESPONSE_LENGTH+1], int);
#ifdef MSLANMAN
bool ms_lanman = 0; /* Use LanMan password instead of NT */
/* Has meaning only with MS-CHAP challenges */
#endif
#if MPPE_SUPPORT
#ifdef DEBUGMPPEKEY
/* For MPPE debug */
/* Use "[]|}{?/><,`!2&&(" (sans quotes) for RFC 3079 MS-CHAPv2 test value */
static char *mschap_challenge = NULL;
/* Use "!@\#$%^&*()_+:3|~" (sans quotes, backslash is to escape #) for ... */
static char *mschap2_peer_challenge = NULL;
#endif
#include "netif/ppp/fsm.h" /* Need to poke MPPE options */
#include "netif/ppp/ccp.h"
#endif /* MPPE_SUPPORT */
#if PPP_OPTIONS
/*
* Command-line options.
*/
static option_t chapms_option_list[] = {
#ifdef MSLANMAN
{ "ms-lanman", o_bool, &ms_lanman,
"Use LanMan passwd when using MS-CHAP", 1 },
#endif
#ifdef DEBUGMPPEKEY
{ "mschap-challenge", o_string, &mschap_challenge,
"specify CHAP challenge" },
{ "mschap2-peer-challenge", o_string, &mschap2_peer_challenge,
"specify CHAP peer challenge" },
#endif
{ NULL }
};
#endif /* PPP_OPTIONS */
#if PPP_SERVER
/*
* chapms_generate_challenge - generate a challenge for MS-CHAP.
* For MS-CHAP the challenge length is fixed at 8 bytes.
* The length goes in challenge[0] and the actual challenge starts
* at challenge[1].
*/
static void chapms_generate_challenge(ppp_pcb *pcb, unsigned char *challenge) {
LWIP_UNUSED_ARG(pcb);
*challenge++ = 8;
#ifdef DEBUGMPPEKEY
if (mschap_challenge && strlen(mschap_challenge) == 8)
memcpy(challenge, mschap_challenge, 8);
else
#endif
magic_random_bytes(challenge, 8);
}
static void chapms2_generate_challenge(ppp_pcb *pcb, unsigned char *challenge) {
LWIP_UNUSED_ARG(pcb);
*challenge++ = 16;
#ifdef DEBUGMPPEKEY
if (mschap_challenge && strlen(mschap_challenge) == 16)
memcpy(challenge, mschap_challenge, 16);
else
#endif
magic_random_bytes(challenge, 16);
}
static int chapms_verify_response(ppp_pcb *pcb, int id, const char *name,
const unsigned char *secret, int secret_len,
const unsigned char *challenge, const unsigned char *response,
char *message, int message_space) {
unsigned char md[MS_CHAP_RESPONSE_LEN];
int diff;
int challenge_len, response_len;
LWIP_UNUSED_ARG(id);
LWIP_UNUSED_ARG(name);
challenge_len = *challenge++; /* skip length, is 8 */
response_len = *response++;
if (response_len != MS_CHAP_RESPONSE_LEN)
goto bad;
#ifndef MSLANMAN
if (!response[MS_CHAP_USENT]) {
/* Should really propagate this into the error packet. */
ppp_notice("Peer request for LANMAN auth not supported");
goto bad;
}
#endif
/* Generate the expected response. */
ChapMS(pcb, (const u_char *)challenge, (const char *)secret, secret_len, md);
#ifdef MSLANMAN
/* Determine which part of response to verify against */
if (!response[MS_CHAP_USENT])
diff = memcmp(&response[MS_CHAP_LANMANRESP],
&md[MS_CHAP_LANMANRESP], MS_CHAP_LANMANRESP_LEN);
else
#endif
diff = memcmp(&response[MS_CHAP_NTRESP], &md[MS_CHAP_NTRESP],
MS_CHAP_NTRESP_LEN);
if (diff == 0) {
ppp_slprintf(message, message_space, "Access granted");
return 1;
}
bad:
/* See comments below for MS-CHAP V2 */
ppp_slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0",
challenge_len, challenge);
return 0;
}
static int chapms2_verify_response(ppp_pcb *pcb, int id, const char *name,
const unsigned char *secret, int secret_len,
const unsigned char *challenge, const unsigned char *response,
char *message, int message_space) {
unsigned char md[MS_CHAP2_RESPONSE_LEN];
char saresponse[MS_AUTH_RESPONSE_LENGTH+1];
int challenge_len, response_len;
LWIP_UNUSED_ARG(id);
challenge_len = *challenge++; /* skip length, is 16 */
response_len = *response++;
if (response_len != MS_CHAP2_RESPONSE_LEN)
goto bad; /* not even the right length */
/* Generate the expected response and our mutual auth. */
ChapMS2(pcb, (const u_char*)challenge, (const u_char*)&response[MS_CHAP2_PEER_CHALLENGE], name,
(const char *)secret, secret_len, md,
(unsigned char *)saresponse, MS_CHAP2_AUTHENTICATOR);
/* compare MDs and send the appropriate status */
/*
* Per RFC 2759, success message must be formatted as
* "S=<auth_string> M=<message>"
* where
* <auth_string> is the Authenticator Response (mutual auth)
* <message> is a text message
*
* However, some versions of Windows (win98 tested) do not know
* about the M=<message> part (required per RFC 2759) and flag
* it as an error (reported incorrectly as an encryption error
* to the user). Since the RFC requires it, and it can be
* useful information, we supply it if the peer is a conforming
* system. Luckily (?), win98 sets the Flags field to 0x04
* (contrary to RFC requirements) so we can use that to
* distinguish between conforming and non-conforming systems.
*
* Special thanks to Alex Swiridov <say@real.kharkov.ua> for
* help debugging this.
*/
if (memcmp(&md[MS_CHAP2_NTRESP], &response[MS_CHAP2_NTRESP],
MS_CHAP2_NTRESP_LEN) == 0) {
if (response[MS_CHAP2_FLAGS])
ppp_slprintf(message, message_space, "S=%s", saresponse);
else
ppp_slprintf(message, message_space, "S=%s M=%s",
saresponse, "Access granted");
return 1;
}
bad:
/*
* Failure message must be formatted as
* "E=e R=r C=c V=v M=m"
* where
* e = error code (we use 691, ERROR_AUTHENTICATION_FAILURE)
* r = retry (we use 1, ok to retry)
* c = challenge to use for next response, we reuse previous
* v = Change Password version supported, we use 0
* m = text message
*
* The M=m part is only for MS-CHAPv2. Neither win2k nor
* win98 (others untested) display the message to the user anyway.
* They also both ignore the E=e code.
*
* Note that it's safe to reuse the same challenge as we don't
* actually accept another response based on the error message
* (and no clients try to resend a response anyway).
*
* Basically, this whole bit is useless code, even the small
* implementation here is only because of overspecification.
*/
ppp_slprintf(message, message_space, "E=691 R=1 C=%0.*B V=0 M=%s",
challenge_len, challenge, "Access denied");
return 0;
}
#endif /* PPP_SERVER */
static void chapms_make_response(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name,
const unsigned char *challenge, const char *secret, int secret_len,
unsigned char *private_) {
LWIP_UNUSED_ARG(id);
LWIP_UNUSED_ARG(our_name);
LWIP_UNUSED_ARG(private_);
challenge++; /* skip length, should be 8 */
*response++ = MS_CHAP_RESPONSE_LEN;
ChapMS(pcb, challenge, secret, secret_len, response);
}
static void chapms2_make_response(ppp_pcb *pcb, unsigned char *response, int id, const char *our_name,
const unsigned char *challenge, const char *secret, int secret_len,
unsigned char *private_) {
LWIP_UNUSED_ARG(id);
challenge++; /* skip length, should be 16 */
*response++ = MS_CHAP2_RESPONSE_LEN;
ChapMS2(pcb, challenge,
#ifdef DEBUGMPPEKEY
mschap2_peer_challenge,
#else
NULL,
#endif
our_name, secret, secret_len, response, private_,
MS_CHAP2_AUTHENTICATEE);
}
static int chapms2_check_success(ppp_pcb *pcb, unsigned char *msg, int len, unsigned char *private_) {
LWIP_UNUSED_ARG(pcb);
if ((len < MS_AUTH_RESPONSE_LENGTH + 2) ||
strncmp((char *)msg, "S=", 2) != 0) {
/* Packet does not start with "S=" */
ppp_error("MS-CHAPv2 Success packet is badly formed.");
return 0;
}
msg += 2;
len -= 2;
if (len < MS_AUTH_RESPONSE_LENGTH
|| memcmp(msg, private_, MS_AUTH_RESPONSE_LENGTH)) {
/* Authenticator Response did not match expected. */
ppp_error("MS-CHAPv2 mutual authentication failed.");
return 0;
}
/* Authenticator Response matches. */
msg += MS_AUTH_RESPONSE_LENGTH; /* Eat it */
len -= MS_AUTH_RESPONSE_LENGTH;
if ((len >= 3) && !strncmp((char *)msg, " M=", 3)) {
msg += 3; /* Eat the delimiter */
} else if (len) {
/* Packet has extra text which does not begin " M=" */
ppp_error("MS-CHAPv2 Success packet is badly formed.");
return 0;
}
return 1;
}
static void chapms_handle_failure(ppp_pcb *pcb, unsigned char *inp, int len) {
int err;
const char *p;
char msg[64];
LWIP_UNUSED_ARG(pcb);
/* We want a null-terminated string for strxxx(). */
len = LWIP_MIN(len, 63);
MEMCPY(msg, inp, len);
msg[len] = 0;
p = msg;
/*
* Deal with MS-CHAP formatted failure messages; just print the
* M=<message> part (if any). For MS-CHAP we're not really supposed
* to use M=<message>, but it shouldn't hurt. See
* chapms[2]_verify_response.
*/
if (!strncmp(p, "E=", 2))
err = strtol(p+2, NULL, 10); /* Remember the error code. */
else
goto print_msg; /* Message is badly formatted. */
if (len && ((p = strstr(p, " M=")) != NULL)) {
/* M=<message> field found. */
p += 3;
} else {
/* No M=<message>; use the error code. */
switch (err) {
case MS_CHAP_ERROR_RESTRICTED_LOGON_HOURS:
p = "E=646 Restricted logon hours";
break;
case MS_CHAP_ERROR_ACCT_DISABLED:
p = "E=647 Account disabled";
break;
case MS_CHAP_ERROR_PASSWD_EXPIRED:
p = "E=648 Password expired";
break;
case MS_CHAP_ERROR_NO_DIALIN_PERMISSION:
p = "E=649 No dialin permission";
break;
case MS_CHAP_ERROR_AUTHENTICATION_FAILURE:
p = "E=691 Authentication failure";
break;
case MS_CHAP_ERROR_CHANGING_PASSWORD:
/* Should never see this, we don't support Change Password. */
p = "E=709 Error changing password";
break;
default:
ppp_error("Unknown MS-CHAP authentication failure: %.*v",
len, inp);
return;
}
}
print_msg:
if (p != NULL)
ppp_error("MS-CHAP authentication failed: %v", p);
}
static void ChallengeResponse(const u_char *challenge,
const u_char PasswordHash[MD4_SIGNATURE_SIZE],
u_char response[24]) {
u_char ZPasswordHash[21];
lwip_des_context des;
u_char des_key[8];
BZERO(ZPasswordHash, sizeof(ZPasswordHash));
MEMCPY(ZPasswordHash, PasswordHash, MD4_SIGNATURE_SIZE);
#if 0
dbglog("ChallengeResponse - ZPasswordHash %.*B",
sizeof(ZPasswordHash), ZPasswordHash);
#endif
pppcrypt_56_to_64_bit_key(ZPasswordHash + 0, des_key);
lwip_des_init(&des);
lwip_des_setkey_enc(&des, des_key);
lwip_des_crypt_ecb(&des, challenge, response +0);
lwip_des_free(&des);
pppcrypt_56_to_64_bit_key(ZPasswordHash + 7, des_key);
lwip_des_init(&des);
lwip_des_setkey_enc(&des, des_key);
lwip_des_crypt_ecb(&des, challenge, response +8);
lwip_des_free(&des);
pppcrypt_56_to_64_bit_key(ZPasswordHash + 14, des_key);
lwip_des_init(&des);
lwip_des_setkey_enc(&des, des_key);
lwip_des_crypt_ecb(&des, challenge, response +16);
lwip_des_free(&des);
#if 0
dbglog("ChallengeResponse - response %.24B", response);
#endif
}
static void ChallengeHash(const u_char PeerChallenge[16], const u_char *rchallenge,
const char *username, u_char Challenge[8]) {
lwip_sha1_context sha1Context;
u_char sha1Hash[SHA1_SIGNATURE_SIZE];
const char *user;
/* remove domain from "domain\username" */
if ((user = strrchr(username, '\\')) != NULL)
++user;
else
user = username;
lwip_sha1_init(&sha1Context);
lwip_sha1_starts(&sha1Context);
lwip_sha1_update(&sha1Context, PeerChallenge, 16);
lwip_sha1_update(&sha1Context, rchallenge, 16);
lwip_sha1_update(&sha1Context, (const unsigned char*)user, strlen(user));
lwip_sha1_finish(&sha1Context, sha1Hash);
lwip_sha1_free(&sha1Context);
MEMCPY(Challenge, sha1Hash, 8);
}
/*
* Convert the ASCII version of the password to Unicode.
* This implicitly supports 8-bit ISO8859/1 characters.
* This gives us the little-endian representation, which
* is assumed by all M$ CHAP RFCs. (Unicode byte ordering
* is machine-dependent.)
*/
static void ascii2unicode(const char ascii[], int ascii_len, u_char unicode[]) {
int i;
BZERO(unicode, ascii_len * 2);
for (i = 0; i < ascii_len; i++)
unicode[i * 2] = (u_char) ascii[i];
}
static void NTPasswordHash(u_char *secret, int secret_len, u_char hash[MD4_SIGNATURE_SIZE]) {
lwip_md4_context md4Context;
lwip_md4_init(&md4Context);
lwip_md4_starts(&md4Context);
lwip_md4_update(&md4Context, secret, secret_len);
lwip_md4_finish(&md4Context, hash);
lwip_md4_free(&md4Context);
}
static void ChapMS_NT(const u_char *rchallenge, const char *secret, int secret_len,
u_char NTResponse[24]) {
u_char unicodePassword[MAX_NT_PASSWORD * 2];
u_char PasswordHash[MD4_SIGNATURE_SIZE];
/* Hash the Unicode version of the secret (== password). */
ascii2unicode(secret, secret_len, unicodePassword);
NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
ChallengeResponse(rchallenge, PasswordHash, NTResponse);
}
static void ChapMS2_NT(const u_char *rchallenge, const u_char PeerChallenge[16], const char *username,
const char *secret, int secret_len, u_char NTResponse[24]) {
u_char unicodePassword[MAX_NT_PASSWORD * 2];
u_char PasswordHash[MD4_SIGNATURE_SIZE];
u_char Challenge[8];
ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
/* Hash the Unicode version of the secret (== password). */
ascii2unicode(secret, secret_len, unicodePassword);
NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
ChallengeResponse(Challenge, PasswordHash, NTResponse);
}
#ifdef MSLANMAN
static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
static void ChapMS_LANMan(u_char *rchallenge, char *secret, int secret_len,
unsigned char *response) {
int i;
u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
u_char PasswordHash[MD4_SIGNATURE_SIZE];
lwip_des_context des;
u_char des_key[8];
/* LANMan password is case insensitive */
BZERO(UcasePassword, sizeof(UcasePassword));
for (i = 0; i < secret_len; i++)
UcasePassword[i] = (u_char)toupper(secret[i]);
pppcrypt_56_to_64_bit_key(UcasePassword +0, des_key);
lwip_des_init(&des);
lwip_des_setkey_enc(&des, des_key);
lwip_des_crypt_ecb(&des, StdText, PasswordHash +0);
lwip_des_free(&des);
pppcrypt_56_to_64_bit_key(UcasePassword +7, des_key);
lwip_des_init(&des);
lwip_des_setkey_enc(&des, des_key);
lwip_des_crypt_ecb(&des, StdText, PasswordHash +8);
lwip_des_free(&des);
ChallengeResponse(rchallenge, PasswordHash, &response[MS_CHAP_LANMANRESP]);
}
#endif
static void GenerateAuthenticatorResponse(const u_char PasswordHashHash[MD4_SIGNATURE_SIZE],
u_char NTResponse[24], const u_char PeerChallenge[16],
const u_char *rchallenge, const char *username,
u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]) {
/*
* "Magic" constants used in response generation, from RFC 2759.
*/
static const u_char Magic1[39] = /* "Magic server to client signing constant" */
{ 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74 };
static const u_char Magic2[41] = /* "Pad to make it do more than one iteration" */
{ 0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
0x6E };
int i;
lwip_sha1_context sha1Context;
u_char Digest[SHA1_SIGNATURE_SIZE];
u_char Challenge[8];
lwip_sha1_init(&sha1Context);
lwip_sha1_starts(&sha1Context);
lwip_sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
lwip_sha1_update(&sha1Context, NTResponse, 24);
lwip_sha1_update(&sha1Context, Magic1, sizeof(Magic1));
lwip_sha1_finish(&sha1Context, Digest);
lwip_sha1_free(&sha1Context);
ChallengeHash(PeerChallenge, rchallenge, username, Challenge);
lwip_sha1_init(&sha1Context);
lwip_sha1_starts(&sha1Context);
lwip_sha1_update(&sha1Context, Digest, sizeof(Digest));
lwip_sha1_update(&sha1Context, Challenge, sizeof(Challenge));
lwip_sha1_update(&sha1Context, Magic2, sizeof(Magic2));
lwip_sha1_finish(&sha1Context, Digest);
lwip_sha1_free(&sha1Context);
/* Convert to ASCII hex string. */
for (i = 0; i < LWIP_MAX((MS_AUTH_RESPONSE_LENGTH / 2), (int)sizeof(Digest)); i++)
sprintf((char *)&authResponse[i * 2], "%02X", Digest[i]);
}
static void GenerateAuthenticatorResponsePlain(
const char *secret, int secret_len,
u_char NTResponse[24], const u_char PeerChallenge[16],
const u_char *rchallenge, const char *username,
u_char authResponse[MS_AUTH_RESPONSE_LENGTH+1]) {
u_char unicodePassword[MAX_NT_PASSWORD * 2];
u_char PasswordHash[MD4_SIGNATURE_SIZE];
u_char PasswordHashHash[MD4_SIGNATURE_SIZE];
/* Hash (x2) the Unicode version of the secret (== password). */
ascii2unicode(secret, secret_len, unicodePassword);
NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
NTPasswordHash(PasswordHash, sizeof(PasswordHash),
PasswordHashHash);
GenerateAuthenticatorResponse(PasswordHashHash, NTResponse, PeerChallenge,
rchallenge, username, authResponse);
}
#if MPPE_SUPPORT
/*
* Set mppe_xxxx_key from MS-CHAP credentials. (see RFC 3079)
*/
static void Set_Start_Key(ppp_pcb *pcb, const u_char *rchallenge, const char *secret, int secret_len) {
u_char unicodePassword[MAX_NT_PASSWORD * 2];
u_char PasswordHash[MD4_SIGNATURE_SIZE];
u_char PasswordHashHash[MD4_SIGNATURE_SIZE];
lwip_sha1_context sha1Context;
u_char Digest[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */
/* Hash (x2) the Unicode version of the secret (== password). */
ascii2unicode(secret, secret_len, unicodePassword);
NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
lwip_sha1_init(&sha1Context);
lwip_sha1_starts(&sha1Context);
lwip_sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
lwip_sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
lwip_sha1_update(&sha1Context, rchallenge, 8);
lwip_sha1_finish(&sha1Context, Digest);
lwip_sha1_free(&sha1Context);
/* Same key in both directions. */
mppe_set_key(pcb, &pcb->mppe_comp, Digest);
mppe_set_key(pcb, &pcb->mppe_decomp, Digest);
pcb->mppe_keys_set = 1;
}
/*
* Set mppe_xxxx_key from MS-CHAPv2 credentials. (see RFC 3079)
*/
static void SetMasterKeys(ppp_pcb *pcb, const char *secret, int secret_len, u_char NTResponse[24], int IsServer) {
u_char unicodePassword[MAX_NT_PASSWORD * 2];
u_char PasswordHash[MD4_SIGNATURE_SIZE];
u_char PasswordHashHash[MD4_SIGNATURE_SIZE];
lwip_sha1_context sha1Context;
u_char MasterKey[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */
u_char Digest[SHA1_SIGNATURE_SIZE]; /* >= MPPE_MAX_KEY_LEN */
const u_char *s;
/* "This is the MPPE Master Key" */
static const u_char Magic1[27] =
{ 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79 };
/* "On the client side, this is the send key; "
"on the server side, it is the receive key." */
static const u_char Magic2[84] =
{ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
0x6b, 0x65, 0x79, 0x2e };
/* "On the client side, this is the receive key; "
"on the server side, it is the send key." */
static const u_char Magic3[84] =
{ 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
0x6b, 0x65, 0x79, 0x2e };
/* Hash (x2) the Unicode version of the secret (== password). */
ascii2unicode(secret, secret_len, unicodePassword);
NTPasswordHash(unicodePassword, secret_len * 2, PasswordHash);
NTPasswordHash(PasswordHash, sizeof(PasswordHash), PasswordHashHash);
lwip_sha1_init(&sha1Context);
lwip_sha1_starts(&sha1Context);
lwip_sha1_update(&sha1Context, PasswordHashHash, MD4_SIGNATURE_SIZE);
lwip_sha1_update(&sha1Context, NTResponse, 24);
lwip_sha1_update(&sha1Context, Magic1, sizeof(Magic1));
lwip_sha1_finish(&sha1Context, MasterKey);
lwip_sha1_free(&sha1Context);
/*
* generate send key
*/
if (IsServer)
s = Magic3;
else
s = Magic2;
lwip_sha1_init(&sha1Context);
lwip_sha1_starts(&sha1Context);
lwip_sha1_update(&sha1Context, MasterKey, 16);
lwip_sha1_update(&sha1Context, mppe_sha1_pad1, SHA1_PAD_SIZE);
lwip_sha1_update(&sha1Context, s, 84);
lwip_sha1_update(&sha1Context, mppe_sha1_pad2, SHA1_PAD_SIZE);
lwip_sha1_finish(&sha1Context, Digest);
lwip_sha1_free(&sha1Context);
mppe_set_key(pcb, &pcb->mppe_comp, Digest);
/*
* generate recv key
*/
if (IsServer)
s = Magic2;
else
s = Magic3;
lwip_sha1_init(&sha1Context);
lwip_sha1_starts(&sha1Context);
lwip_sha1_update(&sha1Context, MasterKey, 16);
lwip_sha1_update(&sha1Context, mppe_sha1_pad1, SHA1_PAD_SIZE);
lwip_sha1_update(&sha1Context, s, 84);
lwip_sha1_update(&sha1Context, mppe_sha1_pad2, SHA1_PAD_SIZE);
lwip_sha1_finish(&sha1Context, Digest);
lwip_sha1_free(&sha1Context);
mppe_set_key(pcb, &pcb->mppe_decomp, Digest);
pcb->mppe_keys_set = 1;
}
#endif /* MPPE_SUPPORT */
static void ChapMS(ppp_pcb *pcb, const u_char *rchallenge, const char *secret, int secret_len,
unsigned char *response) {
#if !MPPE_SUPPORT
LWIP_UNUSED_ARG(pcb);
#endif /* !MPPE_SUPPORT */
BZERO(response, MS_CHAP_RESPONSE_LEN);
ChapMS_NT(rchallenge, secret, secret_len, &response[MS_CHAP_NTRESP]);
#ifdef MSLANMAN
ChapMS_LANMan(rchallenge, secret, secret_len,
&response[MS_CHAP_LANMANRESP]);
/* preferred method is set by option */
response[MS_CHAP_USENT] = !ms_lanman;
#else
response[MS_CHAP_USENT] = 1;
#endif
#if MPPE_SUPPORT
Set_Start_Key(pcb, rchallenge, secret, secret_len);
#endif /* MPPE_SUPPORT */
}
/*
* If PeerChallenge is NULL, one is generated and the PeerChallenge
* field of response is filled in. Call this way when generating a response.
* If PeerChallenge is supplied, it is copied into the PeerChallenge field.
* Call this way when verifying a response (or debugging).
* Do not call with PeerChallenge = response.
*
* The PeerChallenge field of response is then used for calculation of the
* Authenticator Response.
*/
static void ChapMS2(ppp_pcb *pcb, const u_char *rchallenge, const u_char *PeerChallenge,
const char *user, const char *secret, int secret_len, unsigned char *response,
u_char authResponse[], int authenticator) {
/* ARGSUSED */
LWIP_UNUSED_ARG(authenticator);
#if !MPPE_SUPPORT
LWIP_UNUSED_ARG(pcb);
#endif /* !MPPE_SUPPORT */
BZERO(response, MS_CHAP2_RESPONSE_LEN);
/* Generate the Peer-Challenge if requested, or copy it if supplied. */
if (!PeerChallenge)
magic_random_bytes(&response[MS_CHAP2_PEER_CHALLENGE], MS_CHAP2_PEER_CHAL_LEN);
else
MEMCPY(&response[MS_CHAP2_PEER_CHALLENGE], PeerChallenge,
MS_CHAP2_PEER_CHAL_LEN);
/* Generate the NT-Response */
ChapMS2_NT(rchallenge, &response[MS_CHAP2_PEER_CHALLENGE], user,
secret, secret_len, &response[MS_CHAP2_NTRESP]);
/* Generate the Authenticator Response. */
GenerateAuthenticatorResponsePlain(secret, secret_len,
&response[MS_CHAP2_NTRESP],
&response[MS_CHAP2_PEER_CHALLENGE],
rchallenge, user, authResponse);
#if MPPE_SUPPORT
SetMasterKeys(pcb, secret, secret_len,
&response[MS_CHAP2_NTRESP], authenticator);
#endif /* MPPE_SUPPORT */
}
#if 0 /* UNUSED */
#if MPPE_SUPPORT
/*
* Set MPPE options from plugins.
*/
void set_mppe_enc_types(int policy, int types) {
/* Early exit for unknown policies. */
if (policy != MPPE_ENC_POL_ENC_ALLOWED ||
policy != MPPE_ENC_POL_ENC_REQUIRED)
return;
/* Don't modify MPPE if it's optional and wasn't already configured. */
if (policy == MPPE_ENC_POL_ENC_ALLOWED && !ccp_wantoptions[0].mppe)
return;
/*
* Disable undesirable encryption types. Note that we don't ENABLE
* any encryption types, to avoid overriding manual configuration.
*/
switch(types) {
case MPPE_ENC_TYPES_RC4_40:
ccp_wantoptions[0].mppe &= ~MPPE_OPT_128; /* disable 128-bit */
break;
case MPPE_ENC_TYPES_RC4_128:
ccp_wantoptions[0].mppe &= ~MPPE_OPT_40; /* disable 40-bit */
break;
default:
break;
}
}
#endif /* MPPE_SUPPORT */
#endif /* UNUSED */
const struct chap_digest_type chapms_digest = {
CHAP_MICROSOFT, /* code */
#if PPP_SERVER
chapms_generate_challenge,
chapms_verify_response,
#endif /* PPP_SERVER */
chapms_make_response,
NULL, /* check_success */
chapms_handle_failure,
};
const struct chap_digest_type chapms2_digest = {
CHAP_MICROSOFT_V2, /* code */
#if PPP_SERVER
chapms2_generate_challenge,
chapms2_verify_response,
#endif /* PPP_SERVER */
chapms2_make_response,
chapms2_check_success,
chapms_handle_failure,
};
#endif /* PPP_SUPPORT && MSCHAP_SUPPORT */

View File

@@ -0,0 +1,465 @@
/*
* demand.c - Support routines for demand-dialling.
*
* Copyright (c) 1996-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to
* endorse or promote products derived from this software without
* prior written permission.
*
* 3. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Paul Mackerras
* <paulus@samba.org>".
*
* THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT && DEMAND_SUPPORT /* don't build if not configured for use in lwipopts.h */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifdef PPP_FILTER
#include <pcap-bpf.h>
#endif
#include "netif/ppp/ppp_impl.h"
#include "netif/ppp/fsm.h"
#include "netif/ppp/ipcp.h"
#include "netif/ppp/lcp.h"
char *frame;
int framelen;
int framemax;
int escape_flag;
int flush_flag;
int fcs;
struct packet {
int length;
struct packet *next;
unsigned char data[1];
};
struct packet *pend_q;
struct packet *pend_qtail;
static int active_packet (unsigned char *, int);
/*
* demand_conf - configure the interface for doing dial-on-demand.
*/
void
demand_conf()
{
int i;
const struct protent *protp;
/* framemax = lcp_allowoptions[0].mru;
if (framemax < PPP_MRU) */
framemax = PPP_MRU;
framemax += PPP_HDRLEN + PPP_FCSLEN;
frame = malloc(framemax);
if (frame == NULL)
novm("demand frame");
framelen = 0;
pend_q = NULL;
escape_flag = 0;
flush_flag = 0;
fcs = PPP_INITFCS;
netif_set_mtu(pcb, LWIP_MIN(lcp_allowoptions[0].mru, PPP_MRU));
if (ppp_send_config(pcb, PPP_MRU, (u32_t) 0, 0, 0) < 0
|| ppp_recv_config(pcb, PPP_MRU, (u32_t) 0, 0, 0) < 0)
fatal("Couldn't set up demand-dialled PPP interface: %m");
#ifdef PPP_FILTER
set_filters(&pass_filter, &active_filter);
#endif
/*
* Call the demand_conf procedure for each protocol that's got one.
*/
for (i = 0; (protp = protocols[i]) != NULL; ++i)
if (protp->demand_conf != NULL)
((*protp->demand_conf)(pcb));
/* FIXME: find a way to die() here */
#if 0
if (!((*protp->demand_conf)(pcb)))
die(1);
#endif
}
/*
* demand_block - set each network protocol to block further packets.
*/
void
demand_block()
{
int i;
const struct protent *protp;
for (i = 0; (protp = protocols[i]) != NULL; ++i)
if (protp->demand_conf != NULL)
sifnpmode(pcb, protp->protocol & ~0x8000, NPMODE_QUEUE);
get_loop_output();
}
/*
* demand_discard - set each network protocol to discard packets
* with an error.
*/
void
demand_discard()
{
struct packet *pkt, *nextpkt;
int i;
const struct protent *protp;
for (i = 0; (protp = protocols[i]) != NULL; ++i)
if (protp->demand_conf != NULL)
sifnpmode(pcb, protp->protocol & ~0x8000, NPMODE_ERROR);
get_loop_output();
/* discard all saved packets */
for (pkt = pend_q; pkt != NULL; pkt = nextpkt) {
nextpkt = pkt->next;
free(pkt);
}
pend_q = NULL;
framelen = 0;
flush_flag = 0;
escape_flag = 0;
fcs = PPP_INITFCS;
}
/*
* demand_unblock - set each enabled network protocol to pass packets.
*/
void
demand_unblock()
{
int i;
const struct protent *protp;
for (i = 0; (protp = protocols[i]) != NULL; ++i)
if (protp->demand_conf != NULL)
sifnpmode(pcb, protp->protocol & ~0x8000, NPMODE_PASS);
}
/*
* FCS lookup table as calculated by genfcstab.
*/
static u_short fcstab[256] = {
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};
/*
* loop_chars - process characters received from the loopback.
* Calls loop_frame when a complete frame has been accumulated.
* Return value is 1 if we need to bring up the link, 0 otherwise.
*/
int
loop_chars(p, n)
unsigned char *p;
int n;
{
int c, rv;
rv = 0;
/* check for synchronous connection... */
if ( (p[0] == 0xFF) && (p[1] == 0x03) ) {
rv = loop_frame(p,n);
return rv;
}
for (; n > 0; --n) {
c = *p++;
if (c == PPP_FLAG) {
if (!escape_flag && !flush_flag
&& framelen > 2 && fcs == PPP_GOODFCS) {
framelen -= 2;
if (loop_frame((unsigned char *)frame, framelen))
rv = 1;
}
framelen = 0;
flush_flag = 0;
escape_flag = 0;
fcs = PPP_INITFCS;
continue;
}
if (flush_flag)
continue;
if (escape_flag) {
c ^= PPP_TRANS;
escape_flag = 0;
} else if (c == PPP_ESCAPE) {
escape_flag = 1;
continue;
}
if (framelen >= framemax) {
flush_flag = 1;
continue;
}
frame[framelen++] = c;
fcs = PPP_FCS(fcs, c);
}
return rv;
}
/*
* loop_frame - given a frame obtained from the loopback,
* decide whether to bring up the link or not, and, if we want
* to transmit this frame later, put it on the pending queue.
* Return value is 1 if we need to bring up the link, 0 otherwise.
* We assume that the kernel driver has already applied the
* pass_filter, so we won't get packets it rejected.
* We apply the active_filter to see if we want this packet to
* bring up the link.
*/
int
loop_frame(frame, len)
unsigned char *frame;
int len;
{
struct packet *pkt;
/* dbglog("from loop: %P", frame, len); */
if (len < PPP_HDRLEN)
return 0;
if ((PPP_PROTOCOL(frame) & 0x8000) != 0)
return 0; /* shouldn't get any of these anyway */
if (!active_packet(frame, len))
return 0;
pkt = (struct packet *) malloc(sizeof(struct packet) + len);
if (pkt != NULL) {
pkt->length = len;
pkt->next = NULL;
memcpy(pkt->data, frame, len);
if (pend_q == NULL)
pend_q = pkt;
else
pend_qtail->next = pkt;
pend_qtail = pkt;
}
return 1;
}
/*
* demand_rexmit - Resend all those frames which we got via the
* loopback, now that the real serial link is up.
*/
void
demand_rexmit(proto, newip)
int proto;
u32_t newip;
{
struct packet *pkt, *prev, *nextpkt;
unsigned short checksum;
unsigned short pkt_checksum = 0;
unsigned iphdr;
struct timeval tv;
char cv = 0;
char ipstr[16];
prev = NULL;
pkt = pend_q;
pend_q = NULL;
tv.tv_sec = 1;
tv.tv_usec = 0;
select(0,NULL,NULL,NULL,&tv); /* Sleep for 1 Seconds */
for (; pkt != NULL; pkt = nextpkt) {
nextpkt = pkt->next;
if (PPP_PROTOCOL(pkt->data) == proto) {
if ( (proto == PPP_IP) && newip ) {
/* Get old checksum */
iphdr = (pkt->data[4] & 15) << 2;
checksum = *((unsigned short *) (pkt->data+14));
if (checksum == 0xFFFF) {
checksum = 0;
}
if (pkt->data[13] == 17) {
pkt_checksum = *((unsigned short *) (pkt->data+10+iphdr));
if (pkt_checksum) {
cv = 1;
if (pkt_checksum == 0xFFFF) {
pkt_checksum = 0;
}
}
else {
cv = 0;
}
}
if (pkt->data[13] == 6) {
pkt_checksum = *((unsigned short *) (pkt->data+20+iphdr));
cv = 1;
if (pkt_checksum == 0xFFFF) {
pkt_checksum = 0;
}
}
/* Delete old Source-IP-Address */
checksum -= *((unsigned short *) (pkt->data+16)) ^ 0xFFFF;
checksum -= *((unsigned short *) (pkt->data+18)) ^ 0xFFFF;
pkt_checksum -= *((unsigned short *) (pkt->data+16)) ^ 0xFFFF;
pkt_checksum -= *((unsigned short *) (pkt->data+18)) ^ 0xFFFF;
/* Change Source-IP-Address */
* ((u32_t *) (pkt->data + 16)) = newip;
/* Add new Source-IP-Address */
checksum += *((unsigned short *) (pkt->data+16)) ^ 0xFFFF;
checksum += *((unsigned short *) (pkt->data+18)) ^ 0xFFFF;
pkt_checksum += *((unsigned short *) (pkt->data+16)) ^ 0xFFFF;
pkt_checksum += *((unsigned short *) (pkt->data+18)) ^ 0xFFFF;
/* Write new checksum */
if (!checksum) {
checksum = 0xFFFF;
}
*((unsigned short *) (pkt->data+14)) = checksum;
if (pkt->data[13] == 6) {
*((unsigned short *) (pkt->data+20+iphdr)) = pkt_checksum;
}
if (cv && (pkt->data[13] == 17) ) {
*((unsigned short *) (pkt->data+10+iphdr)) = pkt_checksum;
}
/* Log Packet */
strcpy(ipstr,inet_ntoa(*( (struct in_addr *) (pkt->data+16))));
if (pkt->data[13] == 1) {
syslog(LOG_INFO,"Open ICMP %s -> %s\n",
ipstr,
inet_ntoa(*( (struct in_addr *) (pkt->data+20))));
} else {
syslog(LOG_INFO,"Open %s %s:%d -> %s:%d\n",
pkt->data[13] == 6 ? "TCP" : "UDP",
ipstr,
ntohs(*( (short *) (pkt->data+iphdr+4))),
inet_ntoa(*( (struct in_addr *) (pkt->data+20))),
ntohs(*( (short *) (pkt->data+iphdr+6))));
}
}
output(pcb, pkt->data, pkt->length);
free(pkt);
} else {
if (prev == NULL)
pend_q = pkt;
else
prev->next = pkt;
prev = pkt;
}
}
pend_qtail = prev;
if (prev != NULL)
prev->next = NULL;
}
/*
* Scan a packet to decide whether it is an "active" packet,
* that is, whether it is worth bringing up the link for.
*/
static int
active_packet(p, len)
unsigned char *p;
int len;
{
int proto, i;
const struct protent *protp;
if (len < PPP_HDRLEN)
return 0;
proto = PPP_PROTOCOL(p);
#ifdef PPP_FILTER
p[0] = 1; /* outbound packet indicator */
if ((pass_filter.bf_len != 0
&& bpf_filter(pass_filter.bf_insns, p, len, len) == 0)
|| (active_filter.bf_len != 0
&& bpf_filter(active_filter.bf_insns, p, len, len) == 0)) {
p[0] = 0xff;
return 0;
}
p[0] = 0xff;
#endif
for (i = 0; (protp = protocols[i]) != NULL; ++i) {
if (protp->protocol < 0xC000 && (protp->protocol & ~0x8000) == proto) {
if (protp->active_pkt == NULL)
return 1;
return (*protp->active_pkt)(p, len);
}
}
return 0; /* not a supported protocol !!?? */
}
#endif /* PPP_SUPPORT && DEMAND_SUPPORT */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,191 @@
/*
* ecp.c - PPP Encryption Control Protocol.
*
* Copyright (c) 2002 Google, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name(s) of the authors of this software must not be used to
* endorse or promote products derived from this software without
* prior written permission.
*
* THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Derived from ccp.c, which is:
*
* Copyright (c) 1994-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to
* endorse or promote products derived from this software without
* prior written permission.
*
* 3. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Paul Mackerras
* <paulus@samba.org>".
*
* THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT && ECP_SUPPORT /* don't build if not configured for use in lwipopts.h */
#include <string.h>
#include "netif/ppp/ppp_impl.h"
#include "netif/ppp/fsm.h"
#include "netif/ppp/ecp.h"
#if PPP_OPTIONS
static option_t ecp_option_list[] = {
{ "noecp", o_bool, &ecp_protent.enabled_flag,
"Disable ECP negotiation" },
{ "-ecp", o_bool, &ecp_protent.enabled_flag,
"Disable ECP negotiation", OPT_ALIAS },
{ NULL }
};
#endif /* PPP_OPTIONS */
/*
* Protocol entry points from main code.
*/
static void ecp_init (int unit);
/*
static void ecp_open (int unit);
static void ecp_close (int unit, char *);
static void ecp_lowerup (int unit);
static void ecp_lowerdown (int);
static void ecp_input (int unit, u_char *pkt, int len);
static void ecp_protrej (int unit);
*/
#if PRINTPKT_SUPPORT
static int ecp_printpkt (const u_char *pkt, int len,
void (*printer) (void *, char *, ...),
void *arg);
#endif /* PRINTPKT_SUPPORT */
/*
static void ecp_datainput (int unit, u_char *pkt, int len);
*/
const struct protent ecp_protent = {
PPP_ECP,
ecp_init,
NULL, /* ecp_input, */
NULL, /* ecp_protrej, */
NULL, /* ecp_lowerup, */
NULL, /* ecp_lowerdown, */
NULL, /* ecp_open, */
NULL, /* ecp_close, */
#if PRINTPKT_SUPPORT
ecp_printpkt,
#endif /* PRINTPKT_SUPPORT */
#if PPP_DATAINPUT
NULL, /* ecp_datainput, */
#endif /* PPP_DATAINPUT */
#if PRINTPKT_SUPPORT
"ECP",
"Encrypted",
#endif /* PRINTPKT_SUPPORT */
#if PPP_OPTIONS
ecp_option_list,
NULL,
#endif /* PPP_OPTIONS */
#if DEMAND_SUPPORT
NULL,
NULL
#endif /* DEMAND_SUPPORT */
};
fsm ecp_fsm[NUM_PPP];
ecp_options ecp_wantoptions[NUM_PPP]; /* what to request the peer to use */
ecp_options ecp_gotoptions[NUM_PPP]; /* what the peer agreed to do */
ecp_options ecp_allowoptions[NUM_PPP]; /* what we'll agree to do */
ecp_options ecp_hisoptions[NUM_PPP]; /* what we agreed to do */
static const fsm_callbacks ecp_callbacks = {
NULL, /* ecp_resetci, */
NULL, /* ecp_cilen, */
NULL, /* ecp_addci, */
NULL, /* ecp_ackci, */
NULL, /* ecp_nakci, */
NULL, /* ecp_rejci, */
NULL, /* ecp_reqci, */
NULL, /* ecp_up, */
NULL, /* ecp_down, */
NULL,
NULL,
NULL,
NULL,
NULL, /* ecp_extcode, */
"ECP"
};
/*
* ecp_init - initialize ECP.
*/
static void
ecp_init(unit)
int unit;
{
fsm *f = &ecp_fsm[unit];
f->unit = unit;
f->protocol = PPP_ECP;
f->callbacks = &ecp_callbacks;
fsm_init(f);
#if 0 /* Not necessary, everything is cleared in ppp_new() */
memset(&ecp_wantoptions[unit], 0, sizeof(ecp_options));
memset(&ecp_gotoptions[unit], 0, sizeof(ecp_options));
memset(&ecp_allowoptions[unit], 0, sizeof(ecp_options));
memset(&ecp_hisoptions[unit], 0, sizeof(ecp_options));
#endif /* 0 */
}
#if PRINTPKT_SUPPORT
static int
ecp_printpkt(p, plen, printer, arg)
const u_char *p;
int plen;
void (*printer) (void *, char *, ...);
void *arg;
{
return 0;
}
#endif /* PRINTPKT_SUPPORT */
#endif /* PPP_SUPPORT && ECP_SUPPORT */

View File

@@ -0,0 +1,56 @@
/*
* eui64.c - EUI64 routines for IPv6CP.
*
* Copyright (c) 1999 Tommi Komulainen. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name(s) of the authors of this software must not be used to
* endorse or promote products derived from this software without
* prior written permission.
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Tommi Komulainen
* <Tommi.Komulainen@iki.fi>".
*
* THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $Id: eui64.c,v 1.6 2002/12/04 23:03:32 paulus Exp $
*/
#include "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT && PPP_IPV6_SUPPORT /* don't build if not configured for use in lwipopts.h */
#include "netif/ppp/ppp_impl.h"
#include "netif/ppp/eui64.h"
/*
* eui64_ntoa - Make an ascii representation of an interface identifier
*/
char *eui64_ntoa(eui64_t e) {
static char buf[20];
sprintf(buf, "%02x%02x:%02x%02x:%02x%02x:%02x%02x",
e.e8[0], e.e8[1], e.e8[2], e.e8[3],
e.e8[4], e.e8[5], e.e8[6], e.e8[7]);
return buf;
}
#endif /* PPP_SUPPORT && PPP_IPV6_SUPPORT */

View File

@@ -0,0 +1,799 @@
/*
* fsm.c - {Link, IP} Control Protocol Finite State Machine.
*
* Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
/*
* @todo:
* Randomize fsm id on link/init.
* Deal with variable outgoing MTU.
*/
#if 0 /* UNUSED */
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#endif /* UNUSED */
#include "netif/ppp/ppp_impl.h"
#include "netif/ppp/fsm.h"
static void fsm_timeout (void *);
static void fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len);
static void fsm_rconfack(fsm *f, int id, u_char *inp, int len);
static void fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len);
static void fsm_rtermreq(fsm *f, int id, u_char *p, int len);
static void fsm_rtermack(fsm *f);
static void fsm_rcoderej(fsm *f, u_char *inp, int len);
static void fsm_sconfreq(fsm *f, int retransmit);
#define PROTO_NAME(f) ((f)->callbacks->proto_name)
/*
* fsm_init - Initialize fsm.
*
* Initialize fsm state.
*/
void fsm_init(fsm *f) {
ppp_pcb *pcb = f->pcb;
f->state = PPP_FSM_INITIAL;
f->flags = 0;
f->id = 0; /* XXX Start with random id? */
f->maxnakloops = pcb->settings.fsm_max_nak_loops;
f->term_reason_len = 0;
}
/*
* fsm_lowerup - The lower layer is up.
*/
void fsm_lowerup(fsm *f) {
switch( f->state ){
case PPP_FSM_INITIAL:
f->state = PPP_FSM_CLOSED;
break;
case PPP_FSM_STARTING:
if( f->flags & OPT_SILENT )
f->state = PPP_FSM_STOPPED;
else {
/* Send an initial configure-request */
fsm_sconfreq(f, 0);
f->state = PPP_FSM_REQSENT;
}
break;
default:
FSMDEBUG(("%s: Up event in state %d!", PROTO_NAME(f), f->state));
/* no break */
}
}
/*
* fsm_lowerdown - The lower layer is down.
*
* Cancel all timeouts and inform upper layers.
*/
void fsm_lowerdown(fsm *f) {
switch( f->state ){
case PPP_FSM_CLOSED:
f->state = PPP_FSM_INITIAL;
break;
case PPP_FSM_STOPPED:
f->state = PPP_FSM_STARTING;
if( f->callbacks->starting )
(*f->callbacks->starting)(f);
break;
case PPP_FSM_CLOSING:
f->state = PPP_FSM_INITIAL;
UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
break;
case PPP_FSM_STOPPING:
case PPP_FSM_REQSENT:
case PPP_FSM_ACKRCVD:
case PPP_FSM_ACKSENT:
f->state = PPP_FSM_STARTING;
UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
break;
case PPP_FSM_OPENED:
if( f->callbacks->down )
(*f->callbacks->down)(f);
f->state = PPP_FSM_STARTING;
break;
default:
FSMDEBUG(("%s: Down event in state %d!", PROTO_NAME(f), f->state));
/* no break */
}
}
/*
* fsm_open - Link is allowed to come up.
*/
void fsm_open(fsm *f) {
switch( f->state ){
case PPP_FSM_INITIAL:
f->state = PPP_FSM_STARTING;
if( f->callbacks->starting )
(*f->callbacks->starting)(f);
break;
case PPP_FSM_CLOSED:
if( f->flags & OPT_SILENT )
f->state = PPP_FSM_STOPPED;
else {
/* Send an initial configure-request */
fsm_sconfreq(f, 0);
f->state = PPP_FSM_REQSENT;
}
break;
case PPP_FSM_CLOSING:
f->state = PPP_FSM_STOPPING;
/* fall through */
/* no break */
case PPP_FSM_STOPPED:
case PPP_FSM_OPENED:
if( f->flags & OPT_RESTART ){
fsm_lowerdown(f);
fsm_lowerup(f);
}
break;
default:
break;
}
}
/*
* terminate_layer - Start process of shutting down the FSM
*
* Cancel any timeout running, notify upper layers we're done, and
* send a terminate-request message as configured.
*/
static void terminate_layer(fsm *f, int nextstate) {
ppp_pcb *pcb = f->pcb;
if( f->state != PPP_FSM_OPENED )
UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
else if( f->callbacks->down )
(*f->callbacks->down)(f); /* Inform upper layers we're down */
/* Init restart counter and send Terminate-Request */
f->retransmits = pcb->settings.fsm_max_term_transmits;
fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
(const u_char *) f->term_reason, f->term_reason_len);
if (f->retransmits == 0) {
/*
* User asked for no terminate requests at all; just close it.
* We've already fired off one Terminate-Request just to be nice
* to the peer, but we're not going to wait for a reply.
*/
f->state = nextstate == PPP_FSM_CLOSING ? PPP_FSM_CLOSED : PPP_FSM_STOPPED;
if( f->callbacks->finished )
(*f->callbacks->finished)(f);
return;
}
TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time);
--f->retransmits;
f->state = nextstate;
}
/*
* fsm_close - Start closing connection.
*
* Cancel timeouts and either initiate close or possibly go directly to
* the PPP_FSM_CLOSED state.
*/
void fsm_close(fsm *f, const char *reason) {
f->term_reason = reason;
f->term_reason_len = (reason == NULL? 0: (u8_t)LWIP_MIN(strlen(reason), 0xFF) );
switch( f->state ){
case PPP_FSM_STARTING:
f->state = PPP_FSM_INITIAL;
break;
case PPP_FSM_STOPPED:
f->state = PPP_FSM_CLOSED;
break;
case PPP_FSM_STOPPING:
f->state = PPP_FSM_CLOSING;
break;
case PPP_FSM_REQSENT:
case PPP_FSM_ACKRCVD:
case PPP_FSM_ACKSENT:
case PPP_FSM_OPENED:
terminate_layer(f, PPP_FSM_CLOSING);
break;
default:
break;
}
}
/*
* fsm_timeout - Timeout expired.
*/
static void fsm_timeout(void *arg) {
fsm *f = (fsm *) arg;
ppp_pcb *pcb = f->pcb;
switch (f->state) {
case PPP_FSM_CLOSING:
case PPP_FSM_STOPPING:
if( f->retransmits <= 0 ){
/*
* We've waited for an ack long enough. Peer probably heard us.
*/
f->state = (f->state == PPP_FSM_CLOSING)? PPP_FSM_CLOSED: PPP_FSM_STOPPED;
if( f->callbacks->finished )
(*f->callbacks->finished)(f);
} else {
/* Send Terminate-Request */
fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
(const u_char *) f->term_reason, f->term_reason_len);
TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time);
--f->retransmits;
}
break;
case PPP_FSM_REQSENT:
case PPP_FSM_ACKRCVD:
case PPP_FSM_ACKSENT:
if (f->retransmits <= 0) {
ppp_warn("%s: timeout sending Config-Requests", PROTO_NAME(f));
f->state = PPP_FSM_STOPPED;
if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished )
(*f->callbacks->finished)(f);
} else {
/* Retransmit the configure-request */
if (f->callbacks->retransmit)
(*f->callbacks->retransmit)(f);
fsm_sconfreq(f, 1); /* Re-send Configure-Request */
if( f->state == PPP_FSM_ACKRCVD )
f->state = PPP_FSM_REQSENT;
}
break;
default:
FSMDEBUG(("%s: Timeout event in state %d!", PROTO_NAME(f), f->state));
/* no break */
}
}
/*
* fsm_input - Input packet.
*/
void fsm_input(fsm *f, u_char *inpacket, int l) {
u_char *inp;
u_char code, id;
int len;
/*
* Parse header (code, id and length).
* If packet too short, drop it.
*/
inp = inpacket;
if (l < HEADERLEN) {
FSMDEBUG(("fsm_input(%x): Rcvd short header.", f->protocol));
return;
}
GETCHAR(code, inp);
GETCHAR(id, inp);
GETSHORT(len, inp);
if (len < HEADERLEN) {
FSMDEBUG(("fsm_input(%x): Rcvd illegal length.", f->protocol));
return;
}
if (len > l) {
FSMDEBUG(("fsm_input(%x): Rcvd short packet.", f->protocol));
return;
}
len -= HEADERLEN; /* subtract header length */
if( f->state == PPP_FSM_INITIAL || f->state == PPP_FSM_STARTING ){
FSMDEBUG(("fsm_input(%x): Rcvd packet in state %d.",
f->protocol, f->state));
return;
}
/*
* Action depends on code.
*/
switch (code) {
case CONFREQ:
fsm_rconfreq(f, id, inp, len);
break;
case CONFACK:
fsm_rconfack(f, id, inp, len);
break;
case CONFNAK:
case CONFREJ:
fsm_rconfnakrej(f, code, id, inp, len);
break;
case TERMREQ:
fsm_rtermreq(f, id, inp, len);
break;
case TERMACK:
fsm_rtermack(f);
break;
case CODEREJ:
fsm_rcoderej(f, inp, len);
break;
default:
if( !f->callbacks->extcode
|| !(*f->callbacks->extcode)(f, code, id, inp, len) )
fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
break;
}
}
/*
* fsm_rconfreq - Receive Configure-Request.
*/
static void fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len) {
int code, reject_if_disagree;
switch( f->state ){
case PPP_FSM_CLOSED:
/* Go away, we're closed */
fsm_sdata(f, TERMACK, id, NULL, 0);
return;
case PPP_FSM_CLOSING:
case PPP_FSM_STOPPING:
return;
case PPP_FSM_OPENED:
/* Go down and restart negotiation */
if( f->callbacks->down )
(*f->callbacks->down)(f); /* Inform upper layers */
fsm_sconfreq(f, 0); /* Send initial Configure-Request */
f->state = PPP_FSM_REQSENT;
break;
case PPP_FSM_STOPPED:
/* Negotiation started by our peer */
fsm_sconfreq(f, 0); /* Send initial Configure-Request */
f->state = PPP_FSM_REQSENT;
break;
default:
break;
}
/*
* Pass the requested configuration options
* to protocol-specific code for checking.
*/
if (f->callbacks->reqci){ /* Check CI */
reject_if_disagree = (f->nakloops >= f->maxnakloops);
code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
} else if (len)
code = CONFREJ; /* Reject all CI */
else
code = CONFACK;
/* send the Ack, Nak or Rej to the peer */
fsm_sdata(f, code, id, inp, len);
if (code == CONFACK) {
if (f->state == PPP_FSM_ACKRCVD) {
UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
f->state = PPP_FSM_OPENED;
if (f->callbacks->up)
(*f->callbacks->up)(f); /* Inform upper layers */
} else
f->state = PPP_FSM_ACKSENT;
f->nakloops = 0;
} else {
/* we sent CONFACK or CONFREJ */
if (f->state != PPP_FSM_ACKRCVD)
f->state = PPP_FSM_REQSENT;
if( code == CONFNAK )
++f->nakloops;
}
}
/*
* fsm_rconfack - Receive Configure-Ack.
*/
static void fsm_rconfack(fsm *f, int id, u_char *inp, int len) {
ppp_pcb *pcb = f->pcb;
if (id != f->reqid || f->seen_ack) /* Expected id? */
return; /* Nope, toss... */
if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len):
(len == 0)) ){
/* Ack is bad - ignore it */
ppp_error("Received bad configure-ack: %P", inp, len);
return;
}
f->seen_ack = 1;
f->rnakloops = 0;
switch (f->state) {
case PPP_FSM_CLOSED:
case PPP_FSM_STOPPED:
fsm_sdata(f, TERMACK, id, NULL, 0);
break;
case PPP_FSM_REQSENT:
f->state = PPP_FSM_ACKRCVD;
f->retransmits = pcb->settings.fsm_max_conf_req_transmits;
break;
case PPP_FSM_ACKRCVD:
/* Huh? an extra valid Ack? oh well... */
UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
fsm_sconfreq(f, 0);
f->state = PPP_FSM_REQSENT;
break;
case PPP_FSM_ACKSENT:
UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
f->state = PPP_FSM_OPENED;
f->retransmits = pcb->settings.fsm_max_conf_req_transmits;
if (f->callbacks->up)
(*f->callbacks->up)(f); /* Inform upper layers */
break;
case PPP_FSM_OPENED:
/* Go down and restart negotiation */
if (f->callbacks->down)
(*f->callbacks->down)(f); /* Inform upper layers */
fsm_sconfreq(f, 0); /* Send initial Configure-Request */
f->state = PPP_FSM_REQSENT;
break;
default:
break;
}
}
/*
* fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
*/
static void fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len) {
int ret;
int treat_as_reject;
if (id != f->reqid || f->seen_ack) /* Expected id? */
return; /* Nope, toss... */
if (code == CONFNAK) {
++f->rnakloops;
treat_as_reject = (f->rnakloops >= f->maxnakloops);
if (f->callbacks->nakci == NULL
|| !(ret = f->callbacks->nakci(f, inp, len, treat_as_reject))) {
ppp_error("Received bad configure-nak: %P", inp, len);
return;
}
} else {
f->rnakloops = 0;
if (f->callbacks->rejci == NULL
|| !(ret = f->callbacks->rejci(f, inp, len))) {
ppp_error("Received bad configure-rej: %P", inp, len);
return;
}
}
f->seen_ack = 1;
switch (f->state) {
case PPP_FSM_CLOSED:
case PPP_FSM_STOPPED:
fsm_sdata(f, TERMACK, id, NULL, 0);
break;
case PPP_FSM_REQSENT:
case PPP_FSM_ACKSENT:
/* They didn't agree to what we wanted - try another request */
UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
if (ret < 0)
f->state = PPP_FSM_STOPPED; /* kludge for stopping CCP */
else
fsm_sconfreq(f, 0); /* Send Configure-Request */
break;
case PPP_FSM_ACKRCVD:
/* Got a Nak/reject when we had already had an Ack?? oh well... */
UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
fsm_sconfreq(f, 0);
f->state = PPP_FSM_REQSENT;
break;
case PPP_FSM_OPENED:
/* Go down and restart negotiation */
if (f->callbacks->down)
(*f->callbacks->down)(f); /* Inform upper layers */
fsm_sconfreq(f, 0); /* Send initial Configure-Request */
f->state = PPP_FSM_REQSENT;
break;
default:
break;
}
}
/*
* fsm_rtermreq - Receive Terminate-Req.
*/
static void fsm_rtermreq(fsm *f, int id, u_char *p, int len) {
ppp_pcb *pcb = f->pcb;
switch (f->state) {
case PPP_FSM_ACKRCVD:
case PPP_FSM_ACKSENT:
f->state = PPP_FSM_REQSENT; /* Start over but keep trying */
break;
case PPP_FSM_OPENED:
if (len > 0) {
ppp_info("%s terminated by peer (%0.*v)", PROTO_NAME(f), len, p);
} else
ppp_info("%s terminated by peer", PROTO_NAME(f));
f->retransmits = 0;
f->state = PPP_FSM_STOPPING;
if (f->callbacks->down)
(*f->callbacks->down)(f); /* Inform upper layers */
TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time);
break;
default:
break;
}
fsm_sdata(f, TERMACK, id, NULL, 0);
}
/*
* fsm_rtermack - Receive Terminate-Ack.
*/
static void fsm_rtermack(fsm *f) {
switch (f->state) {
case PPP_FSM_CLOSING:
UNTIMEOUT(fsm_timeout, f);
f->state = PPP_FSM_CLOSED;
if( f->callbacks->finished )
(*f->callbacks->finished)(f);
break;
case PPP_FSM_STOPPING:
UNTIMEOUT(fsm_timeout, f);
f->state = PPP_FSM_STOPPED;
if( f->callbacks->finished )
(*f->callbacks->finished)(f);
break;
case PPP_FSM_ACKRCVD:
f->state = PPP_FSM_REQSENT;
break;
case PPP_FSM_OPENED:
if (f->callbacks->down)
(*f->callbacks->down)(f); /* Inform upper layers */
fsm_sconfreq(f, 0);
f->state = PPP_FSM_REQSENT;
break;
default:
break;
}
}
/*
* fsm_rcoderej - Receive an Code-Reject.
*/
static void fsm_rcoderej(fsm *f, u_char *inp, int len) {
u_char code, id;
if (len < HEADERLEN) {
FSMDEBUG(("fsm_rcoderej: Rcvd short Code-Reject packet!"));
return;
}
GETCHAR(code, inp);
GETCHAR(id, inp);
ppp_warn("%s: Rcvd Code-Reject for code %d, id %d", PROTO_NAME(f), code, id);
if( f->state == PPP_FSM_ACKRCVD )
f->state = PPP_FSM_REQSENT;
}
/*
* fsm_protreject - Peer doesn't speak this protocol.
*
* Treat this as a catastrophic error (RXJ-).
*/
void fsm_protreject(fsm *f) {
switch( f->state ){
case PPP_FSM_CLOSING:
UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
/* fall through */
/* no break */
case PPP_FSM_CLOSED:
f->state = PPP_FSM_CLOSED;
if( f->callbacks->finished )
(*f->callbacks->finished)(f);
break;
case PPP_FSM_STOPPING:
case PPP_FSM_REQSENT:
case PPP_FSM_ACKRCVD:
case PPP_FSM_ACKSENT:
UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
/* fall through */
/* no break */
case PPP_FSM_STOPPED:
f->state = PPP_FSM_STOPPED;
if( f->callbacks->finished )
(*f->callbacks->finished)(f);
break;
case PPP_FSM_OPENED:
terminate_layer(f, PPP_FSM_STOPPING);
break;
default:
FSMDEBUG(("%s: Protocol-reject event in state %d!",
PROTO_NAME(f), f->state));
/* no break */
}
}
/*
* fsm_sconfreq - Send a Configure-Request.
*/
static void fsm_sconfreq(fsm *f, int retransmit) {
ppp_pcb *pcb = f->pcb;
struct pbuf *p;
u_char *outp;
int cilen;
if( f->state != PPP_FSM_REQSENT && f->state != PPP_FSM_ACKRCVD && f->state != PPP_FSM_ACKSENT ){
/* Not currently negotiating - reset options */
if( f->callbacks->resetci )
(*f->callbacks->resetci)(f);
f->nakloops = 0;
f->rnakloops = 0;
}
if( !retransmit ){
/* New request - reset retransmission counter, use new ID */
f->retransmits = pcb->settings.fsm_max_conf_req_transmits;
f->reqid = ++f->id;
}
f->seen_ack = 0;
/*
* Make up the request packet
*/
if( f->callbacks->cilen && f->callbacks->addci ){
cilen = (*f->callbacks->cilen)(f);
if( cilen > pcb->peer_mru - HEADERLEN )
cilen = pcb->peer_mru - HEADERLEN;
} else
cilen = 0;
p = pbuf_alloc(PBUF_RAW, (u16_t)(cilen + HEADERLEN + PPP_HDRLEN), PPP_CTRL_PBUF_TYPE);
if(NULL == p)
return;
if(p->tot_len != p->len) {
pbuf_free(p);
return;
}
/* send the request to our peer */
outp = (u_char*)p->payload;
MAKEHEADER(outp, f->protocol);
PUTCHAR(CONFREQ, outp);
PUTCHAR(f->reqid, outp);
PUTSHORT(cilen + HEADERLEN, outp);
if (cilen != 0) {
(*f->callbacks->addci)(f, outp, &cilen);
LWIP_ASSERT("cilen == p->len - HEADERLEN - PPP_HDRLEN", cilen == p->len - HEADERLEN - PPP_HDRLEN);
}
ppp_write(pcb, p);
/* start the retransmit timer */
--f->retransmits;
TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time);
}
/*
* fsm_sdata - Send some data.
*
* Used for all packets sent to our peer by this module.
*/
void fsm_sdata(fsm *f, u_char code, u_char id, const u_char *data, int datalen) {
ppp_pcb *pcb = f->pcb;
struct pbuf *p;
u_char *outp;
int outlen;
/* Adjust length to be smaller than MTU */
if (datalen > pcb->peer_mru - HEADERLEN)
datalen = pcb->peer_mru - HEADERLEN;
outlen = datalen + HEADERLEN;
p = pbuf_alloc(PBUF_RAW, (u16_t)(outlen + PPP_HDRLEN), PPP_CTRL_PBUF_TYPE);
if(NULL == p)
return;
if(p->tot_len != p->len) {
pbuf_free(p);
return;
}
outp = (u_char*)p->payload;
if (datalen) /* && data != outp + PPP_HDRLEN + HEADERLEN) -- was only for fsm_sconfreq() */
MEMCPY(outp + PPP_HDRLEN + HEADERLEN, data, datalen);
MAKEHEADER(outp, f->protocol);
PUTCHAR(code, outp);
PUTCHAR(id, outp);
PUTSHORT(outlen, outp);
ppp_write(pcb, p);
}
#endif /* PPP_SUPPORT */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,294 @@
/*
* magic.c - PPP Magic Number routines.
*
* Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*****************************************************************************
* randm.c - Random number generator program file.
*
* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
* Copyright (c) 1998 by Global Election Systems Inc.
*
* The authors hereby grant permission to use, copy, modify, distribute,
* and license this software and its documentation for any purpose, provided
* that existing copyright notices are retained in all copies and that this
* notice and the following disclaimer are included verbatim in any
* distributions. No written agreement, license, or royalty fee is required
* for any of the authorized uses.
*
* THIS SOFTWARE IS PROVIDED BY THE 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 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.
*
******************************************************************************
* REVISION HISTORY
*
* 03-01-01 Marc Boucher <marc@mbsi.ca>
* Ported to lwIP.
* 98-06-03 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
* Extracted from avos.
*****************************************************************************/
#include "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
#include "netif/ppp/ppp_impl.h"
#include "netif/ppp/magic.h"
#if PPP_MD5_RANDM /* Using MD5 for better randomness if enabled */
#include "netif/ppp/pppcrypt.h"
#define MD5_HASH_SIZE 16
static char magic_randpool[MD5_HASH_SIZE]; /* Pool of randomness. */
static long magic_randcount; /* Pseudo-random incrementer */
static u32_t magic_randomseed; /* Seed used for random number generation. */
/*
* Churn the randomness pool on a random event. Call this early and often
* on random and semi-random system events to build randomness in time for
* usage. For randomly timed events, pass a null pointer and a zero length
* and this will use the system timer and other sources to add randomness.
* If new random data is available, pass a pointer to that and it will be
* included.
*
* Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427
*/
static void magic_churnrand(char *rand_data, u32_t rand_len) {
lwip_md5_context md5_ctx;
/* LWIP_DEBUGF(LOG_INFO, ("magic_churnrand: %u@%P\n", rand_len, rand_data)); */
lwip_md5_init(&md5_ctx);
lwip_md5_starts(&md5_ctx);
lwip_md5_update(&md5_ctx, (u_char *)magic_randpool, sizeof(magic_randpool));
if (rand_data) {
lwip_md5_update(&md5_ctx, (u_char *)rand_data, rand_len);
} else {
struct {
/* INCLUDE fields for any system sources of randomness */
u32_t jiffies;
#ifdef LWIP_RAND
u32_t rand;
#endif /* LWIP_RAND */
} sys_data;
magic_randomseed += sys_jiffies();
sys_data.jiffies = magic_randomseed;
#ifdef LWIP_RAND
sys_data.rand = LWIP_RAND();
#endif /* LWIP_RAND */
/* Load sys_data fields here. */
lwip_md5_update(&md5_ctx, (u_char *)&sys_data, sizeof(sys_data));
}
lwip_md5_finish(&md5_ctx, (u_char *)magic_randpool);
lwip_md5_free(&md5_ctx);
/* LWIP_DEBUGF(LOG_INFO, ("magic_churnrand: -> 0\n")); */
}
/*
* Initialize the random number generator.
*/
void magic_init(void) {
magic_churnrand(NULL, 0);
}
/*
* Randomize our random seed value.
*/
void magic_randomize(void) {
magic_churnrand(NULL, 0);
}
/*
* magic_random_bytes - Fill a buffer with random bytes.
*
* Use the random pool to generate random data. This degrades to pseudo
* random when used faster than randomness is supplied using magic_churnrand().
* Note: It's important that there be sufficient randomness in magic_randpool
* before this is called for otherwise the range of the result may be
* narrow enough to make a search feasible.
*
* Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427
*
* XXX Why does he not just call magic_churnrand() for each block? Probably
* so that you don't ever publish the seed which could possibly help
* predict future values.
* XXX Why don't we preserve md5 between blocks and just update it with
* magic_randcount each time? Probably there is a weakness but I wish that
* it was documented.
*/
void magic_random_bytes(unsigned char *buf, u32_t buf_len) {
lwip_md5_context md5_ctx;
u_char tmp[MD5_HASH_SIZE];
u32_t n;
while (buf_len > 0) {
lwip_md5_init(&md5_ctx);
lwip_md5_starts(&md5_ctx);
lwip_md5_update(&md5_ctx, (u_char *)magic_randpool, sizeof(magic_randpool));
lwip_md5_update(&md5_ctx, (u_char *)&magic_randcount, sizeof(magic_randcount));
lwip_md5_finish(&md5_ctx, tmp);
lwip_md5_free(&md5_ctx);
magic_randcount++;
n = LWIP_MIN(buf_len, MD5_HASH_SIZE);
MEMCPY(buf, tmp, n);
buf += n;
buf_len -= n;
}
}
/*
* Return a new random number.
*/
u32_t magic(void) {
u32_t new_rand;
magic_random_bytes((unsigned char *)&new_rand, sizeof(new_rand));
return new_rand;
}
#else /* PPP_MD5_RANDM */
/*****************************/
/*** LOCAL DATA STRUCTURES ***/
/*****************************/
#ifndef LWIP_RAND
static int magic_randomized; /* Set when truely randomized. */
#endif /* LWIP_RAND */
static u32_t magic_randomseed; /* Seed used for random number generation. */
/***********************************/
/*** PUBLIC FUNCTION DEFINITIONS ***/
/***********************************/
/*
* Initialize the random number generator.
*
* Here we attempt to compute a random number seed but even if
* it isn't random, we'll randomize it later.
*
* The current method uses the fields from the real time clock,
* the idle process counter, the millisecond counter, and the
* hardware timer tick counter. When this is invoked
* in startup(), then the idle counter and timer values may
* repeat after each boot and the real time clock may not be
* operational. Thus we call it again on the first random
* event.
*/
void magic_init(void) {
magic_randomseed += sys_jiffies();
#ifndef LWIP_RAND
/* Initialize the Borland random number generator. */
srand((unsigned)magic_randomseed);
#endif /* LWIP_RAND */
}
/*
* magic_init - Initialize the magic number generator.
*
* Randomize our random seed value. Here we use the fact that
* this function is called at *truely random* times by the polling
* and network functions. Here we only get 16 bits of new random
* value but we use the previous value to randomize the other 16
* bits.
*/
void magic_randomize(void) {
#ifndef LWIP_RAND
if (!magic_randomized) {
magic_randomized = !0;
magic_init();
/* The initialization function also updates the seed. */
} else {
#endif /* LWIP_RAND */
magic_randomseed += sys_jiffies();
#ifndef LWIP_RAND
}
#endif /* LWIP_RAND */
}
/*
* Return a new random number.
*
* Here we use the Borland rand() function to supply a pseudo random
* number which we make truely random by combining it with our own
* seed which is randomized by truely random events.
* Thus the numbers will be truely random unless there have been no
* operator or network events in which case it will be pseudo random
* seeded by the real time clock.
*/
u32_t magic(void) {
#ifdef LWIP_RAND
return LWIP_RAND() + magic_randomseed;
#else /* LWIP_RAND */
return ((u32_t)rand() << 16) + (u32_t)rand() + magic_randomseed;
#endif /* LWIP_RAND */
}
/*
* magic_random_bytes - Fill a buffer with random bytes.
*/
void magic_random_bytes(unsigned char *buf, u32_t buf_len) {
u32_t new_rand, n;
while (buf_len > 0) {
new_rand = magic();
n = LWIP_MIN(buf_len, sizeof(new_rand));
MEMCPY(buf, &new_rand, n);
buf += n;
buf_len -= n;
}
}
#endif /* PPP_MD5_RANDM */
/*
* Return a new random number between 0 and (2^pow)-1 included.
*/
u32_t magic_pow(u8_t pow) {
return magic() & ~(~0UL<<pow);
}
#endif /* PPP_SUPPORT */

View File

@@ -0,0 +1,412 @@
/*
* mppe.c - interface MPPE to the PPP code.
*
* By Frank Cusack <fcusack@fcusack.com>.
* Copyright (c) 2002,2003,2004 Google, Inc.
* All rights reserved.
*
* License:
* Permission to use, copy, modify, and distribute this software and its
* documentation is hereby granted, provided that the above copyright
* notice appears in all copies. This software is provided without any
* warranty, express or implied.
*
* Changelog:
* 08/12/05 - Matt Domsch <Matt_Domsch@dell.com>
* Only need extra skb padding on transmit, not receive.
* 06/18/04 - Matt Domsch <Matt_Domsch@dell.com>, Oleg Makarenko <mole@quadra.ru>
* Use Linux kernel 2.6 arc4 and sha1 routines rather than
* providing our own.
* 2/15/04 - TS: added #include <version.h> and testing for Kernel
* version before using
* MOD_DEC_USAGE_COUNT/MOD_INC_USAGE_COUNT which are
* deprecated in 2.6
*/
#include "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT && MPPE_SUPPORT /* don't build if not configured for use in lwipopts.h */
#include <string.h>
#include "lwip/err.h"
#include "netif/ppp/ppp_impl.h"
#include "netif/ppp/ccp.h"
#include "netif/ppp/mppe.h"
#include "netif/ppp/pppdebug.h"
#include "netif/ppp/pppcrypt.h"
#define SHA1_SIGNATURE_SIZE 20
/* ppp_mppe_state.bits definitions */
#define MPPE_BIT_A 0x80 /* Encryption table were (re)inititalized */
#define MPPE_BIT_B 0x40 /* MPPC only (not implemented) */
#define MPPE_BIT_C 0x20 /* MPPC only (not implemented) */
#define MPPE_BIT_D 0x10 /* This is an encrypted frame */
#define MPPE_BIT_FLUSHED MPPE_BIT_A
#define MPPE_BIT_ENCRYPTED MPPE_BIT_D
#define MPPE_BITS(p) ((p)[0] & 0xf0)
#define MPPE_CCOUNT(p) ((((p)[0] & 0x0f) << 8) + (p)[1])
#define MPPE_CCOUNT_SPACE 0x1000 /* The size of the ccount space */
#define MPPE_OVHD 2 /* MPPE overhead/packet */
#define SANITY_MAX 1600 /* Max bogon factor we will tolerate */
/*
* Perform the MPPE rekey algorithm, from RFC 3078, sec. 7.3.
* Well, not what's written there, but rather what they meant.
*/
static void mppe_rekey(ppp_mppe_state * state, int initial_key)
{
lwip_sha1_context sha1_ctx;
u8_t sha1_digest[SHA1_SIGNATURE_SIZE];
/*
* Key Derivation, from RFC 3078, RFC 3079.
* Equivalent to Get_Key() for MS-CHAP as described in RFC 3079.
*/
lwip_sha1_init(&sha1_ctx);
lwip_sha1_starts(&sha1_ctx);
lwip_sha1_update(&sha1_ctx, state->master_key, state->keylen);
lwip_sha1_update(&sha1_ctx, mppe_sha1_pad1, SHA1_PAD_SIZE);
lwip_sha1_update(&sha1_ctx, state->session_key, state->keylen);
lwip_sha1_update(&sha1_ctx, mppe_sha1_pad2, SHA1_PAD_SIZE);
lwip_sha1_finish(&sha1_ctx, sha1_digest);
lwip_sha1_free(&sha1_ctx);
MEMCPY(state->session_key, sha1_digest, state->keylen);
if (!initial_key) {
lwip_arc4_init(&state->arc4);
lwip_arc4_setup(&state->arc4, sha1_digest, state->keylen);
lwip_arc4_crypt(&state->arc4, state->session_key, state->keylen);
lwip_arc4_free(&state->arc4);
}
if (state->keylen == 8) {
/* See RFC 3078 */
state->session_key[0] = 0xd1;
state->session_key[1] = 0x26;
state->session_key[2] = 0x9e;
}
lwip_arc4_init(&state->arc4);
lwip_arc4_setup(&state->arc4, state->session_key, state->keylen);
}
/*
* Set key, used by MSCHAP before mppe_init() is actually called by CCP so we
* don't have to keep multiple copies of keys.
*/
void mppe_set_key(ppp_pcb *pcb, ppp_mppe_state *state, u8_t *key) {
LWIP_UNUSED_ARG(pcb);
MEMCPY(state->master_key, key, MPPE_MAX_KEY_LEN);
}
/*
* Initialize (de)compressor state.
*/
void
mppe_init(ppp_pcb *pcb, ppp_mppe_state *state, u8_t options)
{
#if PPP_DEBUG
const u8_t *debugstr = (const u8_t*)"mppe_comp_init";
if (&pcb->mppe_decomp == state) {
debugstr = (const u8_t*)"mppe_decomp_init";
}
#endif /* PPP_DEBUG */
/* Save keys. */
MEMCPY(state->session_key, state->master_key, sizeof(state->master_key));
if (options & MPPE_OPT_128)
state->keylen = 16;
else if (options & MPPE_OPT_40)
state->keylen = 8;
else {
PPPDEBUG(LOG_DEBUG, ("%s[%d]: unknown key length\n", debugstr,
pcb->netif->num));
lcp_close(pcb, "MPPE required but peer negotiation failed");
return;
}
if (options & MPPE_OPT_STATEFUL)
state->stateful = 1;
/* Generate the initial session key. */
mppe_rekey(state, 1);
#if PPP_DEBUG
{
int i;
char mkey[sizeof(state->master_key) * 2 + 1];
char skey[sizeof(state->session_key) * 2 + 1];
PPPDEBUG(LOG_DEBUG, ("%s[%d]: initialized with %d-bit %s mode\n",
debugstr, pcb->netif->num, (state->keylen == 16) ? 128 : 40,
(state->stateful) ? "stateful" : "stateless"));
for (i = 0; i < (int)sizeof(state->master_key); i++)
sprintf(mkey + i * 2, "%02x", state->master_key[i]);
for (i = 0; i < (int)sizeof(state->session_key); i++)
sprintf(skey + i * 2, "%02x", state->session_key[i]);
PPPDEBUG(LOG_DEBUG,
("%s[%d]: keys: master: %s initial session: %s\n",
debugstr, pcb->netif->num, mkey, skey));
}
#endif /* PPP_DEBUG */
/*
* Initialize the coherency count. The initial value is not specified
* in RFC 3078, but we can make a reasonable assumption that it will
* start at 0. Setting it to the max here makes the comp/decomp code
* do the right thing (determined through experiment).
*/
state->ccount = MPPE_CCOUNT_SPACE - 1;
/*
* Note that even though we have initialized the key table, we don't
* set the FLUSHED bit. This is contrary to RFC 3078, sec. 3.1.
*/
state->bits = MPPE_BIT_ENCRYPTED;
}
/*
* We received a CCP Reset-Request (actually, we are sending a Reset-Ack),
* tell the compressor to rekey. Note that we MUST NOT rekey for
* every CCP Reset-Request; we only rekey on the next xmit packet.
* We might get multiple CCP Reset-Requests if our CCP Reset-Ack is lost.
* So, rekeying for every CCP Reset-Request is broken as the peer will not
* know how many times we've rekeyed. (If we rekey and THEN get another
* CCP Reset-Request, we must rekey again.)
*/
void mppe_comp_reset(ppp_pcb *pcb, ppp_mppe_state *state)
{
LWIP_UNUSED_ARG(pcb);
state->bits |= MPPE_BIT_FLUSHED;
}
/*
* Compress (encrypt) a packet.
* It's strange to call this a compressor, since the output is always
* MPPE_OVHD + 2 bytes larger than the input.
*/
err_t
mppe_compress(ppp_pcb *pcb, ppp_mppe_state *state, struct pbuf **pb, u16_t protocol)
{
struct pbuf *n, *np;
u8_t *pl;
err_t err;
LWIP_UNUSED_ARG(pcb);
/* TCP stack requires that we don't change the packet payload, therefore we copy
* the whole packet before encryption.
*/
np = pbuf_alloc(PBUF_RAW, MPPE_OVHD + sizeof(protocol) + (*pb)->tot_len, PBUF_RAM);
if (!np) {
return ERR_MEM;
}
/* Hide MPPE header + protocol */
pbuf_remove_header(np, MPPE_OVHD + sizeof(protocol));
if ((err = pbuf_copy(np, *pb)) != ERR_OK) {
pbuf_free(np);
return err;
}
/* Reveal MPPE header + protocol */
pbuf_add_header(np, MPPE_OVHD + sizeof(protocol));
*pb = np;
pl = (u8_t*)np->payload;
state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
PPPDEBUG(LOG_DEBUG, ("mppe_compress[%d]: ccount %d\n", pcb->netif->num, state->ccount));
/* FIXME: use PUT* macros */
pl[0] = state->ccount>>8;
pl[1] = state->ccount;
if (!state->stateful || /* stateless mode */
((state->ccount & 0xff) == 0xff) || /* "flag" packet */
(state->bits & MPPE_BIT_FLUSHED)) { /* CCP Reset-Request */
/* We must rekey */
if (state->stateful) {
PPPDEBUG(LOG_DEBUG, ("mppe_compress[%d]: rekeying\n", pcb->netif->num));
}
mppe_rekey(state, 0);
state->bits |= MPPE_BIT_FLUSHED;
}
pl[0] |= state->bits;
state->bits &= ~MPPE_BIT_FLUSHED; /* reset for next xmit */
pl += MPPE_OVHD;
/* Add protocol */
/* FIXME: add PFC support */
pl[0] = protocol >> 8;
pl[1] = protocol;
/* Hide MPPE header */
pbuf_remove_header(np, MPPE_OVHD);
/* Encrypt packet */
for (n = np; n != NULL; n = n->next) {
lwip_arc4_crypt(&state->arc4, (u8_t*)n->payload, n->len);
if (n->tot_len == n->len) {
break;
}
}
/* Reveal MPPE header */
pbuf_add_header(np, MPPE_OVHD);
return ERR_OK;
}
/*
* We received a CCP Reset-Ack. Just ignore it.
*/
void mppe_decomp_reset(ppp_pcb *pcb, ppp_mppe_state *state)
{
LWIP_UNUSED_ARG(pcb);
LWIP_UNUSED_ARG(state);
return;
}
/*
* Decompress (decrypt) an MPPE packet.
*/
err_t
mppe_decompress(ppp_pcb *pcb, ppp_mppe_state *state, struct pbuf **pb)
{
struct pbuf *n0 = *pb, *n;
u8_t *pl;
u16_t ccount;
u8_t flushed;
/* MPPE Header */
if (n0->len < MPPE_OVHD) {
PPPDEBUG(LOG_DEBUG,
("mppe_decompress[%d]: short pkt (%d)\n",
pcb->netif->num, n0->len));
state->sanity_errors += 100;
goto sanity_error;
}
pl = (u8_t*)n0->payload;
flushed = MPPE_BITS(pl) & MPPE_BIT_FLUSHED;
ccount = MPPE_CCOUNT(pl);
PPPDEBUG(LOG_DEBUG, ("mppe_decompress[%d]: ccount %d\n",
pcb->netif->num, ccount));
/* sanity checks -- terminate with extreme prejudice */
if (!(MPPE_BITS(pl) & MPPE_BIT_ENCRYPTED)) {
PPPDEBUG(LOG_DEBUG,
("mppe_decompress[%d]: ENCRYPTED bit not set!\n",
pcb->netif->num));
state->sanity_errors += 100;
goto sanity_error;
}
if (!state->stateful && !flushed) {
PPPDEBUG(LOG_DEBUG, ("mppe_decompress[%d]: FLUSHED bit not set in "
"stateless mode!\n", pcb->netif->num));
state->sanity_errors += 100;
goto sanity_error;
}
if (state->stateful && ((ccount & 0xff) == 0xff) && !flushed) {
PPPDEBUG(LOG_DEBUG, ("mppe_decompress[%d]: FLUSHED bit not set on "
"flag packet!\n", pcb->netif->num));
state->sanity_errors += 100;
goto sanity_error;
}
/*
* Check the coherency count.
*/
if (!state->stateful) {
/* Discard late packet */
if ((ccount - state->ccount) % MPPE_CCOUNT_SPACE > MPPE_CCOUNT_SPACE / 2) {
state->sanity_errors++;
goto sanity_error;
}
/* RFC 3078, sec 8.1. Rekey for every packet. */
while (state->ccount != ccount) {
mppe_rekey(state, 0);
state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
}
} else {
/* RFC 3078, sec 8.2. */
if (!state->discard) {
/* normal state */
state->ccount = (state->ccount + 1) % MPPE_CCOUNT_SPACE;
if (ccount != state->ccount) {
/*
* (ccount > state->ccount)
* Packet loss detected, enter the discard state.
* Signal the peer to rekey (by sending a CCP Reset-Request).
*/
state->discard = 1;
ccp_resetrequest(pcb);
return ERR_BUF;
}
} else {
/* discard state */
if (!flushed) {
/* ccp.c will be silent (no additional CCP Reset-Requests). */
return ERR_BUF;
} else {
/* Rekey for every missed "flag" packet. */
while ((ccount & ~0xff) !=
(state->ccount & ~0xff)) {
mppe_rekey(state, 0);
state->ccount =
(state->ccount +
256) % MPPE_CCOUNT_SPACE;
}
/* reset */
state->discard = 0;
state->ccount = ccount;
/*
* Another problem with RFC 3078 here. It implies that the
* peer need not send a Reset-Ack packet. But RFC 1962
* requires it. Hopefully, M$ does send a Reset-Ack; even
* though it isn't required for MPPE synchronization, it is
* required to reset CCP state.
*/
}
}
if (flushed)
mppe_rekey(state, 0);
}
/* Hide MPPE header */
pbuf_remove_header(n0, MPPE_OVHD);
/* Decrypt the packet. */
for (n = n0; n != NULL; n = n->next) {
lwip_arc4_crypt(&state->arc4, (u8_t*)n->payload, n->len);
if (n->tot_len == n->len) {
break;
}
}
/* good packet credit */
state->sanity_errors >>= 1;
return ERR_OK;
sanity_error:
if (state->sanity_errors >= SANITY_MAX) {
/*
* Take LCP down if the peer is sending too many bogons.
* We don't want to do this for a single or just a few
* instances since it could just be due to packet corruption.
*/
lcp_close(pcb, "Too many MPPE errors");
}
return ERR_BUF;
}
#endif /* PPP_SUPPORT && MPPE_SUPPORT */

View File

@@ -0,0 +1,609 @@
/*
* multilink.c - support routines for multilink.
*
* Copyright (c) 2000-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to
* endorse or promote products derived from this software without
* prior written permission.
*
* 3. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Paul Mackerras
* <paulus@samba.org>".
*
* THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT && defined(HAVE_MULTILINK) /* don't build if not configured for use in lwipopts.h */
/* Multilink support
*
* Multilink uses Samba TDB (Trivial Database Library), which
* we cannot port, because it needs a filesystem.
*
* We have to choose between doing a memory-shared TDB-clone,
* or dropping multilink support at all.
*/
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <netdb.h>
#include <errno.h>
#include <signal.h>
#include <netinet/in.h>
#include <unistd.h>
#include "netif/ppp/ppp_impl.h"
#include "netif/ppp/fsm.h"
#include "netif/ppp/lcp.h"
#include "netif/ppp/tdb.h"
bool endpoint_specified; /* user gave explicit endpoint discriminator */
char *bundle_id; /* identifier for our bundle */
char *blinks_id; /* key for the list of links */
bool doing_multilink; /* multilink was enabled and agreed to */
bool multilink_master; /* we own the multilink bundle */
extern TDB_CONTEXT *pppdb;
extern char db_key[];
static void make_bundle_links (int append);
static void remove_bundle_link (void);
static void iterate_bundle_links (void (*func) (char *));
static int get_default_epdisc (struct epdisc *);
static int parse_num (char *str, const char *key, int *valp);
static int owns_unit (TDB_DATA pid, int unit);
#define set_ip_epdisc(ep, addr) do { \
ep->length = 4; \
ep->value[0] = addr >> 24; \
ep->value[1] = addr >> 16; \
ep->value[2] = addr >> 8; \
ep->value[3] = addr; \
} while (0)
#define LOCAL_IP_ADDR(addr) \
(((addr) & 0xff000000) == 0x0a000000 /* 10.x.x.x */ \
|| ((addr) & 0xfff00000) == 0xac100000 /* 172.16.x.x */ \
|| ((addr) & 0xffff0000) == 0xc0a80000) /* 192.168.x.x */
#define process_exists(n) (kill((n), 0) == 0 || errno != ESRCH)
void
mp_check_options()
{
lcp_options *wo = &lcp_wantoptions[0];
lcp_options *ao = &lcp_allowoptions[0];
doing_multilink = 0;
if (!multilink)
return;
/* if we're doing multilink, we have to negotiate MRRU */
if (!wo->neg_mrru) {
/* mrru not specified, default to mru */
wo->mrru = wo->mru;
wo->neg_mrru = 1;
}
ao->mrru = ao->mru;
ao->neg_mrru = 1;
if (!wo->neg_endpoint && !noendpoint) {
/* get a default endpoint value */
wo->neg_endpoint = get_default_epdisc(&wo->endpoint);
}
}
/*
* Make a new bundle or join us to an existing bundle
* if we are doing multilink.
*/
int
mp_join_bundle()
{
lcp_options *go = &lcp_gotoptions[0];
lcp_options *ho = &lcp_hisoptions[0];
lcp_options *ao = &lcp_allowoptions[0];
int unit, pppd_pid;
int l, mtu;
char *p;
TDB_DATA key, pid, rec;
if (doing_multilink) {
/* have previously joined a bundle */
if (!go->neg_mrru || !ho->neg_mrru) {
notice("oops, didn't get multilink on renegotiation");
lcp_close(pcb, "multilink required");
return 0;
}
/* XXX should check the peer_authname and ho->endpoint
are the same as previously */
return 0;
}
if (!go->neg_mrru || !ho->neg_mrru) {
/* not doing multilink */
if (go->neg_mrru)
notice("oops, multilink negotiated only for receive");
mtu = ho->neg_mru? ho->mru: PPP_MRU;
if (mtu > ao->mru)
mtu = ao->mru;
if (demand) {
/* already have a bundle */
cfg_bundle(0, 0, 0, 0);
netif_set_mtu(pcb, mtu);
return 0;
}
make_new_bundle(0, 0, 0, 0);
set_ifunit(1);
netif_set_mtu(pcb, mtu);
return 0;
}
doing_multilink = 1;
/*
* Find the appropriate bundle or join a new one.
* First we make up a name for the bundle.
* The length estimate is worst-case assuming every
* character has to be quoted.
*/
l = 4 * strlen(peer_authname) + 10;
if (ho->neg_endpoint)
l += 3 * ho->endpoint.length + 8;
if (bundle_name)
l += 3 * strlen(bundle_name) + 2;
bundle_id = malloc(l);
if (bundle_id == 0)
novm("bundle identifier");
p = bundle_id;
p += slprintf(p, l-1, "BUNDLE=\"%q\"", peer_authname);
if (ho->neg_endpoint || bundle_name)
*p++ = '/';
if (ho->neg_endpoint)
p += slprintf(p, bundle_id+l-p, "%s",
epdisc_to_str(&ho->endpoint));
if (bundle_name)
p += slprintf(p, bundle_id+l-p, "/%v", bundle_name);
/* Make the key for the list of links belonging to the bundle */
l = p - bundle_id;
blinks_id = malloc(l + 7);
if (blinks_id == NULL)
novm("bundle links key");
slprintf(blinks_id, l + 7, "BUNDLE_LINKS=%s", bundle_id + 7);
/*
* For demand mode, we only need to configure the bundle
* and attach the link.
*/
mtu = LWIP_MIN(ho->mrru, ao->mru);
if (demand) {
cfg_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
netif_set_mtu(pcb, mtu);
script_setenv("BUNDLE", bundle_id + 7, 1);
return 0;
}
/*
* Check if the bundle ID is already in the database.
*/
unit = -1;
lock_db();
key.dptr = bundle_id;
key.dsize = p - bundle_id;
pid = tdb_fetch(pppdb, key);
if (pid.dptr != NULL) {
/* bundle ID exists, see if the pppd record exists */
rec = tdb_fetch(pppdb, pid);
if (rec.dptr != NULL && rec.dsize > 0) {
/* make sure the string is null-terminated */
rec.dptr[rec.dsize-1] = 0;
/* parse the interface number */
parse_num(rec.dptr, "IFNAME=ppp", &unit);
/* check the pid value */
if (!parse_num(rec.dptr, "PPPD_PID=", &pppd_pid)
|| !process_exists(pppd_pid)
|| !owns_unit(pid, unit))
unit = -1;
free(rec.dptr);
}
free(pid.dptr);
}
if (unit >= 0) {
/* attach to existing unit */
if (bundle_attach(unit)) {
set_ifunit(0);
script_setenv("BUNDLE", bundle_id + 7, 0);
make_bundle_links(1);
unlock_db();
info("Link attached to %s", ifname);
return 1;
}
/* attach failed because bundle doesn't exist */
}
/* we have to make a new bundle */
make_new_bundle(go->mrru, ho->mrru, go->neg_ssnhf, ho->neg_ssnhf);
set_ifunit(1);
netif_set_mtu(pcb, mtu);
script_setenv("BUNDLE", bundle_id + 7, 1);
make_bundle_links(pcb);
unlock_db();
info("New bundle %s created", ifname);
multilink_master = 1;
return 0;
}
void mp_exit_bundle()
{
lock_db();
remove_bundle_link();
unlock_db();
}
static void sendhup(char *str)
{
int pid;
if (parse_num(str, "PPPD_PID=", &pid) && pid != getpid()) {
if (debug)
dbglog("sending SIGHUP to process %d", pid);
kill(pid, SIGHUP);
}
}
void mp_bundle_terminated()
{
TDB_DATA key;
bundle_terminating = 1;
upper_layers_down(pcb);
notice("Connection terminated.");
#if PPP_STATS_SUPPORT
print_link_stats();
#endif /* PPP_STATS_SUPPORT */
if (!demand) {
remove_pidfiles();
script_unsetenv("IFNAME");
}
lock_db();
destroy_bundle();
iterate_bundle_links(sendhup);
key.dptr = blinks_id;
key.dsize = strlen(blinks_id);
tdb_delete(pppdb, key);
unlock_db();
new_phase(PPP_PHASE_DEAD);
doing_multilink = 0;
multilink_master = 0;
}
static void make_bundle_links(int append)
{
TDB_DATA key, rec;
char *p;
char entry[32];
int l;
key.dptr = blinks_id;
key.dsize = strlen(blinks_id);
slprintf(entry, sizeof(entry), "%s;", db_key);
p = entry;
if (append) {
rec = tdb_fetch(pppdb, key);
if (rec.dptr != NULL && rec.dsize > 0) {
rec.dptr[rec.dsize-1] = 0;
if (strstr(rec.dptr, db_key) != NULL) {
/* already in there? strange */
warn("link entry already exists in tdb");
return;
}
l = rec.dsize + strlen(entry);
p = malloc(l);
if (p == NULL)
novm("bundle link list");
slprintf(p, l, "%s%s", rec.dptr, entry);
} else {
warn("bundle link list not found");
}
if (rec.dptr != NULL)
free(rec.dptr);
}
rec.dptr = p;
rec.dsize = strlen(p) + 1;
if (tdb_store(pppdb, key, rec, TDB_REPLACE))
error("couldn't %s bundle link list",
append? "update": "create");
if (p != entry)
free(p);
}
static void remove_bundle_link()
{
TDB_DATA key, rec;
char entry[32];
char *p, *q;
int l;
key.dptr = blinks_id;
key.dsize = strlen(blinks_id);
slprintf(entry, sizeof(entry), "%s;", db_key);
rec = tdb_fetch(pppdb, key);
if (rec.dptr == NULL || rec.dsize <= 0) {
if (rec.dptr != NULL)
free(rec.dptr);
return;
}
rec.dptr[rec.dsize-1] = 0;
p = strstr(rec.dptr, entry);
if (p != NULL) {
q = p + strlen(entry);
l = strlen(q) + 1;
memmove(p, q, l);
rec.dsize = p - rec.dptr + l;
if (tdb_store(pppdb, key, rec, TDB_REPLACE))
error("couldn't update bundle link list (removal)");
}
free(rec.dptr);
}
static void iterate_bundle_links(void (*func)(char *))
{
TDB_DATA key, rec, pp;
char *p, *q;
key.dptr = blinks_id;
key.dsize = strlen(blinks_id);
rec = tdb_fetch(pppdb, key);
if (rec.dptr == NULL || rec.dsize <= 0) {
error("bundle link list not found (iterating list)");
if (rec.dptr != NULL)
free(rec.dptr);
return;
}
p = rec.dptr;
p[rec.dsize-1] = 0;
while ((q = strchr(p, ';')) != NULL) {
*q = 0;
key.dptr = p;
key.dsize = q - p;
pp = tdb_fetch(pppdb, key);
if (pp.dptr != NULL && pp.dsize > 0) {
pp.dptr[pp.dsize-1] = 0;
func(pp.dptr);
}
if (pp.dptr != NULL)
free(pp.dptr);
p = q + 1;
}
free(rec.dptr);
}
static int
parse_num(str, key, valp)
char *str;
const char *key;
int *valp;
{
char *p, *endp;
int i;
p = strstr(str, key);
if (p != 0) {
p += strlen(key);
i = strtol(p, &endp, 10);
if (endp != p && (*endp == 0 || *endp == ';')) {
*valp = i;
return 1;
}
}
return 0;
}
/*
* Check whether the pppd identified by `key' still owns ppp unit `unit'.
*/
static int
owns_unit(key, unit)
TDB_DATA key;
int unit;
{
char ifkey[32];
TDB_DATA kd, vd;
int ret = 0;
slprintf(ifkey, sizeof(ifkey), "IFNAME=ppp%d", unit);
kd.dptr = ifkey;
kd.dsize = strlen(ifkey);
vd = tdb_fetch(pppdb, kd);
if (vd.dptr != NULL) {
ret = vd.dsize == key.dsize
&& memcmp(vd.dptr, key.dptr, vd.dsize) == 0;
free(vd.dptr);
}
return ret;
}
static int
get_default_epdisc(ep)
struct epdisc *ep;
{
char *p;
struct hostent *hp;
u32_t addr;
/* First try for an ethernet MAC address */
p = get_first_ethernet();
if (p != 0 && get_if_hwaddr(ep->value, p) >= 0) {
ep->class = EPD_MAC;
ep->length = 6;
return 1;
}
/* see if our hostname corresponds to a reasonable IP address */
hp = gethostbyname(hostname);
if (hp != NULL) {
addr = *(u32_t *)hp->h_addr;
if (!bad_ip_adrs(addr)) {
addr = lwip_ntohl(addr);
if (!LOCAL_IP_ADDR(addr)) {
ep->class = EPD_IP;
set_ip_epdisc(ep, addr);
return 1;
}
}
}
return 0;
}
/*
* epdisc_to_str - make a printable string from an endpoint discriminator.
*/
static char *endp_class_names[] = {
"null", "local", "IP", "MAC", "magic", "phone"
};
char *
epdisc_to_str(ep)
struct epdisc *ep;
{
static char str[MAX_ENDP_LEN*3+8];
u_char *p = ep->value;
int i, mask = 0;
char *q, c, c2;
if (ep->class == EPD_NULL && ep->length == 0)
return "null";
if (ep->class == EPD_IP && ep->length == 4) {
u32_t addr;
GETLONG(addr, p);
slprintf(str, sizeof(str), "IP:%I", lwip_htonl(addr));
return str;
}
c = ':';
c2 = '.';
if (ep->class == EPD_MAC && ep->length == 6)
c2 = ':';
else if (ep->class == EPD_MAGIC && (ep->length % 4) == 0)
mask = 3;
q = str;
if (ep->class <= EPD_PHONENUM)
q += slprintf(q, sizeof(str)-1, "%s",
endp_class_names[ep->class]);
else
q += slprintf(q, sizeof(str)-1, "%d", ep->class);
c = ':';
for (i = 0; i < ep->length && i < MAX_ENDP_LEN; ++i) {
if ((i & mask) == 0) {
*q++ = c;
c = c2;
}
q += slprintf(q, str + sizeof(str) - q, "%.2x", ep->value[i]);
}
return str;
}
static int hexc_val(int c)
{
if (c >= 'a')
return c - 'a' + 10;
if (c >= 'A')
return c - 'A' + 10;
return c - '0';
}
int
str_to_epdisc(ep, str)
struct epdisc *ep;
char *str;
{
int i, l;
char *p, *endp;
for (i = EPD_NULL; i <= EPD_PHONENUM; ++i) {
int sl = strlen(endp_class_names[i]);
if (strncasecmp(str, endp_class_names[i], sl) == 0) {
str += sl;
break;
}
}
if (i > EPD_PHONENUM) {
/* not a class name, try a decimal class number */
i = strtol(str, &endp, 10);
if (endp == str)
return 0; /* can't parse class number */
str = endp;
}
ep->class = i;
if (*str == 0) {
ep->length = 0;
return 1;
}
if (*str != ':' && *str != '.')
return 0;
++str;
if (i == EPD_IP) {
u32_t addr;
i = parse_dotted_ip(str, &addr);
if (i == 0 || str[i] != 0)
return 0;
set_ip_epdisc(ep, addr);
return 1;
}
if (i == EPD_MAC && get_if_hwaddr(ep->value, str) >= 0) {
ep->length = 6;
return 1;
}
p = str;
for (l = 0; l < MAX_ENDP_LEN; ++l) {
if (*str == 0)
break;
if (p <= str)
for (p = str; isxdigit(*p); ++p)
;
i = p - str;
if (i == 0)
return 0;
ep->value[l] = hexc_val(*str++);
if ((i & 1) == 0)
ep->value[l] = (ep->value[l] << 4) + hexc_val(*str++);
if (*str == ':' || *str == '.')
++str;
}
if (*str != 0 || (ep->class == EPD_MAC && l != 6))
return 0;
ep->length = l;
return 1;
}
#endif /* PPP_SUPPORT && HAVE_MULTILINK */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,427 @@
/**
* @file
* Point To Point Protocol Sequential API module
*
*/
/*
* 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. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
*
* This file is part of the lwIP TCP/IP stack.
*
*/
#include "netif/ppp/ppp_opts.h"
#if LWIP_PPP_API /* don't build if not configured for use in lwipopts.h */
#include "netif/ppp/pppapi.h"
#include "lwip/priv/tcpip_priv.h"
#include "netif/ppp/pppoe.h"
#include "netif/ppp/pppol2tp.h"
#include "netif/ppp/pppos.h"
#if LWIP_MPU_COMPATIBLE
LWIP_MEMPOOL_DECLARE(PPPAPI_MSG, MEMP_NUM_PPP_API_MSG, sizeof(struct pppapi_msg), "PPPAPI_MSG")
#endif
#define PPPAPI_VAR_REF(name) API_VAR_REF(name)
#define PPPAPI_VAR_DECLARE(name) API_VAR_DECLARE(struct pppapi_msg, name)
#define PPPAPI_VAR_ALLOC(name) API_VAR_ALLOC_POOL(struct pppapi_msg, PPPAPI_MSG, name, ERR_MEM)
#define PPPAPI_VAR_ALLOC_RETURN_NULL(name) API_VAR_ALLOC_POOL(struct pppapi_msg, PPPAPI_MSG, name, NULL)
#define PPPAPI_VAR_FREE(name) API_VAR_FREE_POOL(PPPAPI_MSG, name)
/**
* Call ppp_set_default() inside the tcpip_thread context.
*/
static err_t
pppapi_do_ppp_set_default(struct tcpip_api_call_data *m)
{
/* cast through void* to silence alignment warnings.
* We know it works because the structs have been instantiated as struct pppapi_msg */
struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
ppp_set_default(msg->msg.ppp);
return ERR_OK;
}
/**
* Call ppp_set_default() in a thread-safe way by running that function inside the
* tcpip_thread context.
*/
err_t
pppapi_set_default(ppp_pcb *pcb)
{
err_t err;
PPPAPI_VAR_DECLARE(msg);
PPPAPI_VAR_ALLOC(msg);
PPPAPI_VAR_REF(msg).msg.ppp = pcb;
err = tcpip_api_call(pppapi_do_ppp_set_default, &PPPAPI_VAR_REF(msg).call);
PPPAPI_VAR_FREE(msg);
return err;
}
#if PPP_NOTIFY_PHASE
/**
* Call ppp_set_notify_phase_callback() inside the tcpip_thread context.
*/
static err_t
pppapi_do_ppp_set_notify_phase_callback(struct tcpip_api_call_data *m)
{
/* cast through void* to silence alignment warnings.
* We know it works because the structs have been instantiated as struct pppapi_msg */
struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
ppp_set_notify_phase_callback(msg->msg.ppp, msg->msg.msg.setnotifyphasecb.notify_phase_cb);
return ERR_OK;
}
/**
* Call ppp_set_notify_phase_callback() in a thread-safe way by running that function inside the
* tcpip_thread context.
*/
err_t
pppapi_set_notify_phase_callback(ppp_pcb *pcb, ppp_notify_phase_cb_fn notify_phase_cb)
{
err_t err;
PPPAPI_VAR_DECLARE(msg);
PPPAPI_VAR_ALLOC(msg);
PPPAPI_VAR_REF(msg).msg.ppp = pcb;
PPPAPI_VAR_REF(msg).msg.msg.setnotifyphasecb.notify_phase_cb = notify_phase_cb;
err = tcpip_api_call(pppapi_do_ppp_set_notify_phase_callback, &PPPAPI_VAR_REF(msg).call);
PPPAPI_VAR_FREE(msg);
return err;
}
#endif /* PPP_NOTIFY_PHASE */
#if PPPOS_SUPPORT
/**
* Call pppos_create() inside the tcpip_thread context.
*/
static err_t
pppapi_do_pppos_create(struct tcpip_api_call_data *m)
{
/* cast through void* to silence alignment warnings.
* We know it works because the structs have been instantiated as struct pppapi_msg */
struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
msg->msg.ppp = pppos_create(msg->msg.msg.serialcreate.pppif, msg->msg.msg.serialcreate.output_cb,
msg->msg.msg.serialcreate.link_status_cb, msg->msg.msg.serialcreate.ctx_cb);
return ERR_OK;
}
/**
* Call pppos_create() in a thread-safe way by running that function inside the
* tcpip_thread context.
*/
ppp_pcb*
pppapi_pppos_create(struct netif *pppif, pppos_output_cb_fn output_cb,
ppp_link_status_cb_fn link_status_cb, void *ctx_cb)
{
ppp_pcb* result;
PPPAPI_VAR_DECLARE(msg);
PPPAPI_VAR_ALLOC_RETURN_NULL(msg);
PPPAPI_VAR_REF(msg).msg.ppp = NULL;
PPPAPI_VAR_REF(msg).msg.msg.serialcreate.pppif = pppif;
PPPAPI_VAR_REF(msg).msg.msg.serialcreate.output_cb = output_cb;
PPPAPI_VAR_REF(msg).msg.msg.serialcreate.link_status_cb = link_status_cb;
PPPAPI_VAR_REF(msg).msg.msg.serialcreate.ctx_cb = ctx_cb;
tcpip_api_call(pppapi_do_pppos_create, &PPPAPI_VAR_REF(msg).call);
result = PPPAPI_VAR_REF(msg).msg.ppp;
PPPAPI_VAR_FREE(msg);
return result;
}
#endif /* PPPOS_SUPPORT */
#if PPPOE_SUPPORT
/**
* Call pppoe_create() inside the tcpip_thread context.
*/
static err_t
pppapi_do_pppoe_create(struct tcpip_api_call_data *m)
{
/* cast through void* to silence alignment warnings.
* We know it works because the structs have been instantiated as struct pppapi_msg */
struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
msg->msg.ppp = pppoe_create(msg->msg.msg.ethernetcreate.pppif, msg->msg.msg.ethernetcreate.ethif,
msg->msg.msg.ethernetcreate.service_name, msg->msg.msg.ethernetcreate.concentrator_name,
msg->msg.msg.ethernetcreate.link_status_cb, msg->msg.msg.ethernetcreate.ctx_cb);
return ERR_OK;
}
/**
* Call pppoe_create() in a thread-safe way by running that function inside the
* tcpip_thread context.
*/
ppp_pcb*
pppapi_pppoe_create(struct netif *pppif, struct netif *ethif, const char *service_name,
const char *concentrator_name, ppp_link_status_cb_fn link_status_cb,
void *ctx_cb)
{
ppp_pcb* result;
PPPAPI_VAR_DECLARE(msg);
PPPAPI_VAR_ALLOC_RETURN_NULL(msg);
PPPAPI_VAR_REF(msg).msg.ppp = NULL;
PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.pppif = pppif;
PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.ethif = ethif;
PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.service_name = service_name;
PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.concentrator_name = concentrator_name;
PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.link_status_cb = link_status_cb;
PPPAPI_VAR_REF(msg).msg.msg.ethernetcreate.ctx_cb = ctx_cb;
tcpip_api_call(pppapi_do_pppoe_create, &PPPAPI_VAR_REF(msg).call);
result = PPPAPI_VAR_REF(msg).msg.ppp;
PPPAPI_VAR_FREE(msg);
return result;
}
#endif /* PPPOE_SUPPORT */
#if PPPOL2TP_SUPPORT
/**
* Call pppol2tp_create() inside the tcpip_thread context.
*/
static err_t
pppapi_do_pppol2tp_create(struct tcpip_api_call_data *m)
{
/* cast through void* to silence alignment warnings.
* We know it works because the structs have been instantiated as struct pppapi_msg */
struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
msg->msg.ppp = pppol2tp_create(msg->msg.msg.l2tpcreate.pppif,
msg->msg.msg.l2tpcreate.netif, API_EXPR_REF(msg->msg.msg.l2tpcreate.ipaddr), msg->msg.msg.l2tpcreate.port,
#if PPPOL2TP_AUTH_SUPPORT
msg->msg.msg.l2tpcreate.secret,
msg->msg.msg.l2tpcreate.secret_len,
#else /* PPPOL2TP_AUTH_SUPPORT */
NULL,
0,
#endif /* PPPOL2TP_AUTH_SUPPORT */
msg->msg.msg.l2tpcreate.link_status_cb, msg->msg.msg.l2tpcreate.ctx_cb);
return ERR_OK;
}
/**
* Call pppol2tp_create() in a thread-safe way by running that function inside the
* tcpip_thread context.
*/
ppp_pcb*
pppapi_pppol2tp_create(struct netif *pppif, struct netif *netif, ip_addr_t *ipaddr, u16_t port,
const u8_t *secret, u8_t secret_len,
ppp_link_status_cb_fn link_status_cb, void *ctx_cb)
{
ppp_pcb* result;
PPPAPI_VAR_DECLARE(msg);
PPPAPI_VAR_ALLOC_RETURN_NULL(msg);
#if !PPPOL2TP_AUTH_SUPPORT
LWIP_UNUSED_ARG(secret);
LWIP_UNUSED_ARG(secret_len);
#endif /* !PPPOL2TP_AUTH_SUPPORT */
PPPAPI_VAR_REF(msg).msg.ppp = NULL;
PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.pppif = pppif;
PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.netif = netif;
PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.ipaddr = PPPAPI_VAR_REF(ipaddr);
PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.port = port;
#if PPPOL2TP_AUTH_SUPPORT
PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.secret = secret;
PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.secret_len = secret_len;
#endif /* PPPOL2TP_AUTH_SUPPORT */
PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.link_status_cb = link_status_cb;
PPPAPI_VAR_REF(msg).msg.msg.l2tpcreate.ctx_cb = ctx_cb;
tcpip_api_call(pppapi_do_pppol2tp_create, &PPPAPI_VAR_REF(msg).call);
result = PPPAPI_VAR_REF(msg).msg.ppp;
PPPAPI_VAR_FREE(msg);
return result;
}
#endif /* PPPOL2TP_SUPPORT */
/**
* Call ppp_connect() inside the tcpip_thread context.
*/
static err_t
pppapi_do_ppp_connect(struct tcpip_api_call_data *m)
{
/* cast through void* to silence alignment warnings.
* We know it works because the structs have been instantiated as struct pppapi_msg */
struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
return ppp_connect(msg->msg.ppp, msg->msg.msg.connect.holdoff);
}
/**
* Call ppp_connect() in a thread-safe way by running that function inside the
* tcpip_thread context.
*/
err_t
pppapi_connect(ppp_pcb *pcb, u16_t holdoff)
{
err_t err;
PPPAPI_VAR_DECLARE(msg);
PPPAPI_VAR_ALLOC(msg);
PPPAPI_VAR_REF(msg).msg.ppp = pcb;
PPPAPI_VAR_REF(msg).msg.msg.connect.holdoff = holdoff;
err = tcpip_api_call(pppapi_do_ppp_connect, &PPPAPI_VAR_REF(msg).call);
PPPAPI_VAR_FREE(msg);
return err;
}
#if PPP_SERVER
/**
* Call ppp_listen() inside the tcpip_thread context.
*/
static err_t
pppapi_do_ppp_listen(struct tcpip_api_call_data *m)
{
/* cast through void* to silence alignment warnings.
* We know it works because the structs have been instantiated as struct pppapi_msg */
struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
return ppp_listen(msg->msg.ppp);
}
/**
* Call ppp_listen() in a thread-safe way by running that function inside the
* tcpip_thread context.
*/
err_t
pppapi_listen(ppp_pcb *pcb)
{
err_t err;
PPPAPI_VAR_DECLARE(msg);
PPPAPI_VAR_ALLOC(msg);
PPPAPI_VAR_REF(msg).msg.ppp = pcb;
err = tcpip_api_call(pppapi_do_ppp_listen, &PPPAPI_VAR_REF(msg).call);
PPPAPI_VAR_FREE(msg);
return err;
}
#endif /* PPP_SERVER */
/**
* Call ppp_close() inside the tcpip_thread context.
*/
static err_t
pppapi_do_ppp_close(struct tcpip_api_call_data *m)
{
/* cast through void* to silence alignment warnings.
* We know it works because the structs have been instantiated as struct pppapi_msg */
struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
return ppp_close(msg->msg.ppp, msg->msg.msg.close.nocarrier);
}
/**
* Call ppp_close() in a thread-safe way by running that function inside the
* tcpip_thread context.
*/
err_t
pppapi_close(ppp_pcb *pcb, u8_t nocarrier)
{
err_t err;
PPPAPI_VAR_DECLARE(msg);
PPPAPI_VAR_ALLOC(msg);
PPPAPI_VAR_REF(msg).msg.ppp = pcb;
PPPAPI_VAR_REF(msg).msg.msg.close.nocarrier = nocarrier;
err = tcpip_api_call(pppapi_do_ppp_close, &PPPAPI_VAR_REF(msg).call);
PPPAPI_VAR_FREE(msg);
return err;
}
/**
* Call ppp_free() inside the tcpip_thread context.
*/
static err_t
pppapi_do_ppp_free(struct tcpip_api_call_data *m)
{
/* cast through void* to silence alignment warnings.
* We know it works because the structs have been instantiated as struct pppapi_msg */
struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
return ppp_free(msg->msg.ppp);
}
/**
* Call ppp_free() in a thread-safe way by running that function inside the
* tcpip_thread context.
*/
err_t
pppapi_free(ppp_pcb *pcb)
{
err_t err;
PPPAPI_VAR_DECLARE(msg);
PPPAPI_VAR_ALLOC(msg);
PPPAPI_VAR_REF(msg).msg.ppp = pcb;
err = tcpip_api_call(pppapi_do_ppp_free, &PPPAPI_VAR_REF(msg).call);
PPPAPI_VAR_FREE(msg);
return err;
}
/**
* Call ppp_ioctl() inside the tcpip_thread context.
*/
static err_t
pppapi_do_ppp_ioctl(struct tcpip_api_call_data *m)
{
/* cast through void* to silence alignment warnings.
* We know it works because the structs have been instantiated as struct pppapi_msg */
struct pppapi_msg *msg = (struct pppapi_msg *)(void*)m;
return ppp_ioctl(msg->msg.ppp, msg->msg.msg.ioctl.cmd, msg->msg.msg.ioctl.arg);
}
/**
* Call ppp_ioctl() in a thread-safe way by running that function inside the
* tcpip_thread context.
*/
err_t
pppapi_ioctl(ppp_pcb *pcb, u8_t cmd, void *arg)
{
err_t err;
PPPAPI_VAR_DECLARE(msg);
PPPAPI_VAR_ALLOC(msg);
PPPAPI_VAR_REF(msg).msg.ppp = pcb;
PPPAPI_VAR_REF(msg).msg.msg.ioctl.cmd = cmd;
PPPAPI_VAR_REF(msg).msg.msg.ioctl.arg = arg;
err = tcpip_api_call(pppapi_do_ppp_ioctl, &PPPAPI_VAR_REF(msg).call);
PPPAPI_VAR_FREE(msg);
return err;
}
#endif /* LWIP_PPP_API */

View File

@@ -0,0 +1,66 @@
/*
* pppcrypt.c - PPP/DES linkage for MS-CHAP and EAP SRP-SHA1
*
* Extracted from chap_ms.c by James Carlson.
*
* Copyright (c) 1995 Eric Rosenquist. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name(s) of the authors of this software must not be used to
* endorse or promote products derived from this software without
* prior written permission.
*
* THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT && MSCHAP_SUPPORT /* don't build if not necessary */
#include "netif/ppp/ppp_impl.h"
#include "netif/ppp/pppcrypt.h"
static u_char pppcrypt_get_7bits(u_char *input, int startBit) {
unsigned int word;
word = (unsigned)input[startBit / 8] << 8;
word |= (unsigned)input[startBit / 8 + 1];
word >>= 15 - (startBit % 8 + 7);
return word & 0xFE;
}
/* IN 56 bit DES key missing parity bits
* OUT 64 bit DES key with parity bits added
*/
void pppcrypt_56_to_64_bit_key(u_char *key, u_char * des_key) {
des_key[0] = pppcrypt_get_7bits(key, 0);
des_key[1] = pppcrypt_get_7bits(key, 7);
des_key[2] = pppcrypt_get_7bits(key, 14);
des_key[3] = pppcrypt_get_7bits(key, 21);
des_key[4] = pppcrypt_get_7bits(key, 28);
des_key[5] = pppcrypt_get_7bits(key, 35);
des_key[6] = pppcrypt_get_7bits(key, 42);
des_key[7] = pppcrypt_get_7bits(key, 49);
}
#endif /* PPP_SUPPORT && MSCHAP_SUPPORT */

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,895 @@
/**
* @file
* Network Point to Point Protocol over Serial file.
*
*/
/*
* 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. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
*
* This file is part of the lwIP TCP/IP stack.
*
*/
#include "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT && PPPOS_SUPPORT /* don't build if not configured for use in lwipopts.h */
#include <string.h>
#include "lwip/arch.h"
#include "lwip/err.h"
#include "lwip/pbuf.h"
#include "lwip/sys.h"
#include "lwip/memp.h"
#include "lwip/netif.h"
#include "lwip/snmp.h"
#include "lwip/priv/tcpip_priv.h"
#include "lwip/api.h"
#include "lwip/ip4.h" /* for ip4_input() */
#include "netif/ppp/ppp_impl.h"
#include "netif/ppp/pppos.h"
#include "netif/ppp/vj.h"
/* Memory pool */
LWIP_MEMPOOL_DECLARE(PPPOS_PCB, MEMP_NUM_PPPOS_INTERFACES, sizeof(pppos_pcb), "PPPOS_PCB")
/* callbacks called from PPP core */
static err_t pppos_write(ppp_pcb *ppp, void *ctx, struct pbuf *p);
static err_t pppos_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *pb, u16_t protocol);
static void pppos_connect(ppp_pcb *ppp, void *ctx);
#if PPP_SERVER
static void pppos_listen(ppp_pcb *ppp, void *ctx);
#endif /* PPP_SERVER */
static void pppos_disconnect(ppp_pcb *ppp, void *ctx);
static err_t pppos_destroy(ppp_pcb *ppp, void *ctx);
static void pppos_send_config(ppp_pcb *ppp, void *ctx, u32_t accm, int pcomp, int accomp);
static void pppos_recv_config(ppp_pcb *ppp, void *ctx, u32_t accm, int pcomp, int accomp);
/* Prototypes for procedures local to this file. */
#if PPP_INPROC_IRQ_SAFE
static void pppos_input_callback(void *arg);
#endif /* PPP_INPROC_IRQ_SAFE */
static void pppos_input_free_current_packet(pppos_pcb *pppos);
static void pppos_input_drop(pppos_pcb *pppos);
static err_t pppos_output_append(pppos_pcb *pppos, err_t err, struct pbuf *nb, u8_t c, u8_t accm, u16_t *fcs);
static err_t pppos_output_last(pppos_pcb *pppos, err_t err, struct pbuf *nb, u16_t *fcs);
/* Callbacks structure for PPP core */
static const struct link_callbacks pppos_callbacks = {
pppos_connect,
#if PPP_SERVER
pppos_listen,
#endif /* PPP_SERVER */
pppos_disconnect,
pppos_destroy,
pppos_write,
pppos_netif_output,
pppos_send_config,
pppos_recv_config
};
/* PPP's Asynchronous-Control-Character-Map. The mask array is used
* to select the specific bit for a character. */
#define ESCAPE_P(accm, c) ((accm)[(c) >> 3] & 1 << (c & 0x07))
#if PPP_FCS_TABLE
/*
* FCS lookup table as calculated by genfcstab.
*/
static const u16_t fcstab[256] = {
0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};
#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff])
#else /* PPP_FCS_TABLE */
/* The HDLC polynomial: X**0 + X**5 + X**12 + X**16 (0x8408) */
#define PPP_FCS_POLYNOMIAL 0x8408
static u16_t
ppp_get_fcs(u8_t byte)
{
unsigned int octet;
int bit;
octet = byte;
for (bit = 8; bit-- > 0; ) {
octet = (octet & 0x01) ? ((octet >> 1) ^ PPP_FCS_POLYNOMIAL) : (octet >> 1);
}
return octet & 0xffff;
}
#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ ppp_get_fcs(((fcs) ^ (c)) & 0xff))
#endif /* PPP_FCS_TABLE */
/*
* Values for FCS calculations.
*/
#define PPP_INITFCS 0xffff /* Initial FCS value */
#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */
#if PPP_INPROC_IRQ_SAFE
#define PPPOS_DECL_PROTECT(lev) SYS_ARCH_DECL_PROTECT(lev)
#define PPPOS_PROTECT(lev) SYS_ARCH_PROTECT(lev)
#define PPPOS_UNPROTECT(lev) SYS_ARCH_UNPROTECT(lev)
#else
#define PPPOS_DECL_PROTECT(lev)
#define PPPOS_PROTECT(lev)
#define PPPOS_UNPROTECT(lev)
#endif /* PPP_INPROC_IRQ_SAFE */
/*
* Create a new PPP connection using the given serial I/O device.
*
* Return 0 on success, an error code on failure.
*/
ppp_pcb *pppos_create(struct netif *pppif, pppos_output_cb_fn output_cb,
ppp_link_status_cb_fn link_status_cb, void *ctx_cb)
{
pppos_pcb *pppos;
ppp_pcb *ppp;
LWIP_ASSERT_CORE_LOCKED();
pppos = (pppos_pcb *)LWIP_MEMPOOL_ALLOC(PPPOS_PCB);
if (pppos == NULL) {
return NULL;
}
ppp = ppp_new(pppif, &pppos_callbacks, pppos, link_status_cb, ctx_cb);
if (ppp == NULL) {
LWIP_MEMPOOL_FREE(PPPOS_PCB, pppos);
return NULL;
}
memset(pppos, 0, sizeof(pppos_pcb));
pppos->ppp = ppp;
pppos->output_cb = output_cb;
return ppp;
}
/* Called by PPP core */
static err_t
pppos_write(ppp_pcb *ppp, void *ctx, struct pbuf *p)
{
pppos_pcb *pppos = (pppos_pcb *)ctx;
u8_t *s;
struct pbuf *nb;
u16_t n;
u16_t fcs_out;
err_t err;
LWIP_UNUSED_ARG(ppp);
/* Grab an output buffer. Using PBUF_POOL here for tx is ok since the pbuf
gets freed by 'pppos_output_last' before this function returns and thus
cannot starve rx. */
nb = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
if (nb == NULL) {
PPPDEBUG(LOG_WARNING, ("pppos_write[%d]: alloc fail\n", ppp->netif->num));
LINK_STATS_INC(link.memerr);
LINK_STATS_INC(link.drop);
MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
pbuf_free(p);
return ERR_MEM;
}
/* Set nb->tot_len to actual payload length */
nb->tot_len = p->len;
/* If the link has been idle, we'll send a fresh flag character to
* flush any noise. */
err = ERR_OK;
if ((sys_now() - pppos->last_xmit) >= PPP_MAXIDLEFLAG) {
err = pppos_output_append(pppos, err, nb, PPP_FLAG, 0, NULL);
}
/* Load output buffer. */
fcs_out = PPP_INITFCS;
s = (u8_t*)p->payload;
n = p->len;
while (n-- > 0) {
err = pppos_output_append(pppos, err, nb, *s++, 1, &fcs_out);
}
err = pppos_output_last(pppos, err, nb, &fcs_out);
if (err == ERR_OK) {
PPPDEBUG(LOG_INFO, ("pppos_write[%d]: len=%d\n", ppp->netif->num, p->len));
} else {
PPPDEBUG(LOG_WARNING, ("pppos_write[%d]: output failed len=%d\n", ppp->netif->num, p->len));
}
pbuf_free(p);
return err;
}
/* Called by PPP core */
static err_t
pppos_netif_output(ppp_pcb *ppp, void *ctx, struct pbuf *pb, u16_t protocol)
{
pppos_pcb *pppos = (pppos_pcb *)ctx;
struct pbuf *nb, *p;
u16_t fcs_out;
err_t err;
LWIP_UNUSED_ARG(ppp);
/* Grab an output buffer. Using PBUF_POOL here for tx is ok since the pbuf
gets freed by 'pppos_output_last' before this function returns and thus
cannot starve rx. */
nb = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
if (nb == NULL) {
PPPDEBUG(LOG_WARNING, ("pppos_netif_output[%d]: alloc fail\n", ppp->netif->num));
LINK_STATS_INC(link.memerr);
LINK_STATS_INC(link.drop);
MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
return ERR_MEM;
}
/* Set nb->tot_len to actual payload length */
nb->tot_len = pb->tot_len;
/* If the link has been idle, we'll send a fresh flag character to
* flush any noise. */
err = ERR_OK;
if ((sys_now() - pppos->last_xmit) >= PPP_MAXIDLEFLAG) {
err = pppos_output_append(pppos, err, nb, PPP_FLAG, 0, NULL);
}
fcs_out = PPP_INITFCS;
if (!pppos->accomp) {
err = pppos_output_append(pppos, err, nb, PPP_ALLSTATIONS, 1, &fcs_out);
err = pppos_output_append(pppos, err, nb, PPP_UI, 1, &fcs_out);
}
if (!pppos->pcomp || protocol > 0xFF) {
err = pppos_output_append(pppos, err, nb, (protocol >> 8) & 0xFF, 1, &fcs_out);
}
err = pppos_output_append(pppos, err, nb, protocol & 0xFF, 1, &fcs_out);
/* Load packet. */
for(p = pb; p; p = p->next) {
u16_t n = p->len;
u8_t *s = (u8_t*)p->payload;
while (n-- > 0) {
err = pppos_output_append(pppos, err, nb, *s++, 1, &fcs_out);
}
}
err = pppos_output_last(pppos, err, nb, &fcs_out);
if (err == ERR_OK) {
PPPDEBUG(LOG_INFO, ("pppos_netif_output[%d]: proto=0x%"X16_F", len = %d\n", ppp->netif->num, protocol, pb->tot_len));
} else {
PPPDEBUG(LOG_WARNING, ("pppos_netif_output[%d]: output failed proto=0x%"X16_F", len = %d\n", ppp->netif->num, protocol, pb->tot_len));
}
return err;
}
static void
pppos_connect(ppp_pcb *ppp, void *ctx)
{
pppos_pcb *pppos = (pppos_pcb *)ctx;
PPPOS_DECL_PROTECT(lev);
#if PPP_INPROC_IRQ_SAFE
/* input pbuf left over from last session? */
pppos_input_free_current_packet(pppos);
#endif /* PPP_INPROC_IRQ_SAFE */
/* reset PPPoS control block to its initial state */
memset(&pppos->last_xmit, 0, sizeof(pppos_pcb) - offsetof(pppos_pcb, last_xmit));
/*
* Default the in and out accm so that escape and flag characters
* are always escaped.
*/
pppos->in_accm[15] = 0x60; /* no need to protect since RX is not running */
pppos->out_accm[15] = 0x60;
PPPOS_PROTECT(lev);
pppos->open = 1;
PPPOS_UNPROTECT(lev);
/*
* Start the connection and handle incoming events (packet or timeout).
*/
PPPDEBUG(LOG_INFO, ("pppos_connect: unit %d: connecting\n", ppp->netif->num));
ppp_start(ppp); /* notify upper layers */
}
#if PPP_SERVER
static void
pppos_listen(ppp_pcb *ppp, void *ctx)
{
pppos_pcb *pppos = (pppos_pcb *)ctx;
PPPOS_DECL_PROTECT(lev);
#if PPP_INPROC_IRQ_SAFE
/* input pbuf left over from last session? */
pppos_input_free_current_packet(pppos);
#endif /* PPP_INPROC_IRQ_SAFE */
/* reset PPPoS control block to its initial state */
memset(&pppos->last_xmit, 0, sizeof(pppos_pcb) - offsetof(pppos_pcb, last_xmit));
/*
* Default the in and out accm so that escape and flag characters
* are always escaped.
*/
pppos->in_accm[15] = 0x60; /* no need to protect since RX is not running */
pppos->out_accm[15] = 0x60;
PPPOS_PROTECT(lev);
pppos->open = 1;
PPPOS_UNPROTECT(lev);
/*
* Wait for something to happen.
*/
PPPDEBUG(LOG_INFO, ("pppos_listen: unit %d: listening\n", ppp->netif->num));
ppp_start(ppp); /* notify upper layers */
}
#endif /* PPP_SERVER */
static void
pppos_disconnect(ppp_pcb *ppp, void *ctx)
{
pppos_pcb *pppos = (pppos_pcb *)ctx;
PPPOS_DECL_PROTECT(lev);
PPPOS_PROTECT(lev);
pppos->open = 0;
PPPOS_UNPROTECT(lev);
/* If PPP_INPROC_IRQ_SAFE is used we cannot call
* pppos_input_free_current_packet() here because
* rx IRQ might still call pppos_input().
*/
#if !PPP_INPROC_IRQ_SAFE
/* input pbuf left ? */
pppos_input_free_current_packet(pppos);
#endif /* !PPP_INPROC_IRQ_SAFE */
ppp_link_end(ppp); /* notify upper layers */
}
static err_t
pppos_destroy(ppp_pcb *ppp, void *ctx)
{
pppos_pcb *pppos = (pppos_pcb *)ctx;
LWIP_UNUSED_ARG(ppp);
#if PPP_INPROC_IRQ_SAFE
/* input pbuf left ? */
pppos_input_free_current_packet(pppos);
#endif /* PPP_INPROC_IRQ_SAFE */
LWIP_MEMPOOL_FREE(PPPOS_PCB, pppos);
return ERR_OK;
}
#if !NO_SYS && !PPP_INPROC_IRQ_SAFE
/** Pass received raw characters to PPPoS to be decoded through lwIP TCPIP thread.
*
* This is one of the only functions that may be called outside of the TCPIP thread!
*
* @param ppp PPP descriptor index, returned by pppos_create()
* @param s received data
* @param l length of received data
*/
err_t
pppos_input_tcpip(ppp_pcb *ppp, u8_t *s, int l)
{
struct pbuf *p;
err_t err;
p = pbuf_alloc(PBUF_RAW, l, PBUF_POOL);
if (!p) {
return ERR_MEM;
}
pbuf_take(p, s, l);
err = tcpip_inpkt(p, ppp_netif(ppp), pppos_input_sys);
if (err != ERR_OK) {
pbuf_free(p);
}
return err;
}
/* called from TCPIP thread */
err_t pppos_input_sys(struct pbuf *p, struct netif *inp) {
ppp_pcb *ppp = (ppp_pcb*)inp->state;
struct pbuf *n;
LWIP_ASSERT_CORE_LOCKED();
for (n = p; n; n = n->next) {
pppos_input(ppp, (u8_t*)n->payload, n->len);
}
pbuf_free(p);
return ERR_OK;
}
#endif /* !NO_SYS && !PPP_INPROC_IRQ_SAFE */
/** PPPoS input helper struct, must be packed since it is stored
* to pbuf->payload, which might be unaligned. */
#if PPP_INPROC_IRQ_SAFE
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/bpstruct.h"
#endif
PACK_STRUCT_BEGIN
struct pppos_input_header {
PACK_STRUCT_FIELD(ppp_pcb *ppp);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/epstruct.h"
#endif
#endif /* PPP_INPROC_IRQ_SAFE */
/** Pass received raw characters to PPPoS to be decoded.
*
* @param ppp PPP descriptor index, returned by pppos_create()
* @param s received data
* @param l length of received data
*/
void
pppos_input(ppp_pcb *ppp, u8_t *s, int l)
{
pppos_pcb *pppos = (pppos_pcb *)ppp->link_ctx_cb;
struct pbuf *next_pbuf;
u8_t cur_char;
u8_t escaped;
PPPOS_DECL_PROTECT(lev);
#if !PPP_INPROC_IRQ_SAFE
LWIP_ASSERT_CORE_LOCKED();
#endif
PPPDEBUG(LOG_DEBUG, ("pppos_input[%d]: got %d bytes\n", ppp->netif->num, l));
while (l-- > 0) {
cur_char = *s++;
PPPOS_PROTECT(lev);
/* ppp_input can disconnect the interface, we need to abort to prevent a memory
* leak if there are remaining bytes because pppos_connect and pppos_listen
* functions expect input buffer to be free. Furthermore there are no real
* reason to continue reading bytes if we are disconnected.
*/
if (!pppos->open) {
PPPOS_UNPROTECT(lev);
return;
}
escaped = ESCAPE_P(pppos->in_accm, cur_char);
PPPOS_UNPROTECT(lev);
/* Handle special characters. */
if (escaped) {
/* Check for escape sequences. */
/* XXX Note that this does not handle an escaped 0x5d character which
* would appear as an escape character. Since this is an ASCII ']'
* and there is no reason that I know of to escape it, I won't complicate
* the code to handle this case. GLL */
if (cur_char == PPP_ESCAPE) {
pppos->in_escaped = 1;
/* Check for the flag character. */
} else if (cur_char == PPP_FLAG) {
/* If this is just an extra flag character, ignore it. */
if (pppos->in_state <= PDADDRESS) {
/* ignore it */;
/* If we haven't received the packet header, drop what has come in. */
} else if (pppos->in_state < PDDATA) {
PPPDEBUG(LOG_WARNING,
("pppos_input[%d]: Dropping incomplete packet %d\n",
ppp->netif->num, pppos->in_state));
LINK_STATS_INC(link.lenerr);
pppos_input_drop(pppos);
/* If the fcs is invalid, drop the packet. */
} else if (pppos->in_fcs != PPP_GOODFCS) {
PPPDEBUG(LOG_INFO,
("pppos_input[%d]: Dropping bad fcs 0x%"X16_F" proto=0x%"X16_F"\n",
ppp->netif->num, pppos->in_fcs, pppos->in_protocol));
/* Note: If you get lots of these, check for UART frame errors or try different baud rate */
LINK_STATS_INC(link.chkerr);
pppos_input_drop(pppos);
/* Otherwise it's a good packet so pass it on. */
} else {
struct pbuf *inp;
/* Trim off the checksum. */
if(pppos->in_tail->len > 2) {
pppos->in_tail->len -= 2;
pppos->in_tail->tot_len = pppos->in_tail->len;
if (pppos->in_tail != pppos->in_head) {
pbuf_cat(pppos->in_head, pppos->in_tail);
}
} else {
pppos->in_tail->tot_len = pppos->in_tail->len;
if (pppos->in_tail != pppos->in_head) {
pbuf_cat(pppos->in_head, pppos->in_tail);
}
pbuf_realloc(pppos->in_head, pppos->in_head->tot_len - 2);
}
/* Dispatch the packet thereby consuming it. */
inp = pppos->in_head;
/* Packet consumed, release our references. */
pppos->in_head = NULL;
pppos->in_tail = NULL;
#if IP_FORWARD || LWIP_IPV6_FORWARD
/* hide the room for Ethernet forwarding header */
pbuf_remove_header(inp, PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN);
#endif /* IP_FORWARD || LWIP_IPV6_FORWARD */
#if PPP_INPROC_IRQ_SAFE
if(tcpip_try_callback(pppos_input_callback, inp) != ERR_OK) {
PPPDEBUG(LOG_ERR, ("pppos_input[%d]: tcpip_callback() failed, dropping packet\n", ppp->netif->num));
pbuf_free(inp);
LINK_STATS_INC(link.drop);
MIB2_STATS_NETIF_INC(ppp->netif, ifindiscards);
}
#else /* PPP_INPROC_IRQ_SAFE */
ppp_input(ppp, inp);
#endif /* PPP_INPROC_IRQ_SAFE */
}
/* Prepare for a new packet. */
pppos->in_fcs = PPP_INITFCS;
pppos->in_state = PDADDRESS;
pppos->in_escaped = 0;
/* Other characters are usually control characters that may have
* been inserted by the physical layer so here we just drop them. */
} else {
PPPDEBUG(LOG_WARNING,
("pppos_input[%d]: Dropping ACCM char <%d>\n", ppp->netif->num, cur_char));
}
/* Process other characters. */
} else {
/* Unencode escaped characters. */
if (pppos->in_escaped) {
pppos->in_escaped = 0;
cur_char ^= PPP_TRANS;
}
/* Process character relative to current state. */
switch(pppos->in_state) {
case PDIDLE: /* Idle state - waiting. */
/* Drop the character if it's not 0xff
* we would have processed a flag character above. */
if (cur_char != PPP_ALLSTATIONS) {
break;
}
/* no break */
/* Fall through */
case PDSTART: /* Process start flag. */
/* Prepare for a new packet. */
pppos->in_fcs = PPP_INITFCS;
/* no break */
/* Fall through */
case PDADDRESS: /* Process address field. */
if (cur_char == PPP_ALLSTATIONS) {
pppos->in_state = PDCONTROL;
break;
}
/* no break */
/* Else assume compressed address and control fields so
* fall through to get the protocol... */
/* Fall through */
case PDCONTROL: /* Process control field. */
/* If we don't get a valid control code, restart. */
if (cur_char == PPP_UI) {
pppos->in_state = PDPROTOCOL1;
break;
}
/* no break */
#if 0
else {
PPPDEBUG(LOG_WARNING,
("pppos_input[%d]: Invalid control <%d>\n", ppp->netif->num, cur_char));
pppos->in_state = PDSTART;
}
#endif
/* Fall through */
case PDPROTOCOL1: /* Process protocol field 1. */
/* If the lower bit is set, this is the end of the protocol
* field. */
if (cur_char & 1) {
pppos->in_protocol = cur_char;
pppos->in_state = PDDATA;
} else {
pppos->in_protocol = (u16_t)cur_char << 8;
pppos->in_state = PDPROTOCOL2;
}
break;
case PDPROTOCOL2: /* Process protocol field 2. */
pppos->in_protocol |= cur_char;
pppos->in_state = PDDATA;
break;
case PDDATA: /* Process data byte. */
/* Make space to receive processed data. */
if (pppos->in_tail == NULL || pppos->in_tail->len == PBUF_POOL_BUFSIZE) {
u16_t pbuf_alloc_len;
if (pppos->in_tail != NULL) {
pppos->in_tail->tot_len = pppos->in_tail->len;
if (pppos->in_tail != pppos->in_head) {
pbuf_cat(pppos->in_head, pppos->in_tail);
/* give up the in_tail reference now */
pppos->in_tail = NULL;
}
}
/* If we haven't started a packet, we need a packet header. */
pbuf_alloc_len = 0;
#if IP_FORWARD || LWIP_IPV6_FORWARD
/* If IP forwarding is enabled we are reserving PBUF_LINK_ENCAPSULATION_HLEN
* + PBUF_LINK_HLEN bytes so the packet is being allocated with enough header
* space to be forwarded (to Ethernet for example).
*/
if (pppos->in_head == NULL) {
pbuf_alloc_len = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN;
}
#endif /* IP_FORWARD || LWIP_IPV6_FORWARD */
next_pbuf = pbuf_alloc(PBUF_RAW, pbuf_alloc_len, PBUF_POOL);
if (next_pbuf == NULL) {
/* No free buffers. Drop the input packet and let the
* higher layers deal with it. Continue processing
* the received pbuf chain in case a new packet starts. */
PPPDEBUG(LOG_ERR, ("pppos_input[%d]: NO FREE PBUFS!\n", ppp->netif->num));
LINK_STATS_INC(link.memerr);
pppos_input_drop(pppos);
pppos->in_state = PDSTART; /* Wait for flag sequence. */
break;
}
if (pppos->in_head == NULL) {
u8_t *payload = ((u8_t*)next_pbuf->payload) + pbuf_alloc_len;
#if PPP_INPROC_IRQ_SAFE
((struct pppos_input_header*)payload)->ppp = ppp;
payload += sizeof(struct pppos_input_header);
next_pbuf->len += sizeof(struct pppos_input_header);
#endif /* PPP_INPROC_IRQ_SAFE */
next_pbuf->len += sizeof(pppos->in_protocol);
*(payload++) = pppos->in_protocol >> 8;
*(payload) = pppos->in_protocol & 0xFF;
pppos->in_head = next_pbuf;
}
pppos->in_tail = next_pbuf;
}
/* Load character into buffer. */
((u8_t*)pppos->in_tail->payload)[pppos->in_tail->len++] = cur_char;
break;
default:
break;
}
/* update the frame check sequence number. */
pppos->in_fcs = PPP_FCS(pppos->in_fcs, cur_char);
}
} /* while (l-- > 0), all bytes processed */
}
#if PPP_INPROC_IRQ_SAFE
/* PPPoS input callback using one input pointer
*/
static void pppos_input_callback(void *arg) {
struct pbuf *pb = (struct pbuf*)arg;
ppp_pcb *ppp;
ppp = ((struct pppos_input_header*)pb->payload)->ppp;
if(pbuf_remove_header(pb, sizeof(struct pppos_input_header))) {
LWIP_ASSERT("pbuf_remove_header failed\n", 0);
goto drop;
}
/* Dispatch the packet thereby consuming it. */
ppp_input(ppp, pb);
return;
drop:
LINK_STATS_INC(link.drop);
MIB2_STATS_NETIF_INC(ppp->netif, ifindiscards);
pbuf_free(pb);
}
#endif /* PPP_INPROC_IRQ_SAFE */
static void
pppos_send_config(ppp_pcb *ppp, void *ctx, u32_t accm, int pcomp, int accomp)
{
int i;
pppos_pcb *pppos = (pppos_pcb *)ctx;
LWIP_UNUSED_ARG(ppp);
pppos->pcomp = pcomp;
pppos->accomp = accomp;
/* Load the ACCM bits for the 32 control codes. */
for (i = 0; i < 32/8; i++) {
pppos->out_accm[i] = (u8_t)((accm >> (8 * i)) & 0xFF);
}
PPPDEBUG(LOG_INFO, ("pppos_send_config[%d]: out_accm=%X %X %X %X\n",
pppos->ppp->netif->num,
pppos->out_accm[0], pppos->out_accm[1], pppos->out_accm[2], pppos->out_accm[3]));
}
static void
pppos_recv_config(ppp_pcb *ppp, void *ctx, u32_t accm, int pcomp, int accomp)
{
int i;
pppos_pcb *pppos = (pppos_pcb *)ctx;
PPPOS_DECL_PROTECT(lev);
LWIP_UNUSED_ARG(ppp);
LWIP_UNUSED_ARG(pcomp);
LWIP_UNUSED_ARG(accomp);
/* Load the ACCM bits for the 32 control codes. */
PPPOS_PROTECT(lev);
for (i = 0; i < 32 / 8; i++) {
pppos->in_accm[i] = (u8_t)(accm >> (i * 8));
}
PPPOS_UNPROTECT(lev);
PPPDEBUG(LOG_INFO, ("pppos_recv_config[%d]: in_accm=%X %X %X %X\n",
pppos->ppp->netif->num,
pppos->in_accm[0], pppos->in_accm[1], pppos->in_accm[2], pppos->in_accm[3]));
}
/*
* Drop the input packet.
*/
static void
pppos_input_free_current_packet(pppos_pcb *pppos)
{
if (pppos->in_head != NULL) {
if (pppos->in_tail && (pppos->in_tail != pppos->in_head)) {
pbuf_free(pppos->in_tail);
}
pbuf_free(pppos->in_head);
pppos->in_head = NULL;
}
pppos->in_tail = NULL;
}
/*
* Drop the input packet and increase error counters.
*/
static void
pppos_input_drop(pppos_pcb *pppos)
{
if (pppos->in_head != NULL) {
#if 0
PPPDEBUG(LOG_INFO, ("pppos_input_drop: %d:%.*H\n", pppos->in_head->len, min(60, pppos->in_head->len * 2), pppos->in_head->payload));
#endif
PPPDEBUG(LOG_INFO, ("pppos_input_drop: pbuf len=%d, addr %p\n", pppos->in_head->len, (void*)pppos->in_head));
}
pppos_input_free_current_packet(pppos);
#if VJ_SUPPORT
vj_uncompress_err(&pppos->ppp->vj_comp);
#endif /* VJ_SUPPORT */
LINK_STATS_INC(link.drop);
MIB2_STATS_NETIF_INC(pppos->ppp->netif, ifindiscards);
}
/*
* pppos_output_append - append given character to end of given pbuf.
* If out_accm is not 0 and the character needs to be escaped, do so.
* If pbuf is full, send the pbuf and reuse it.
* Return the current pbuf.
*/
static err_t
pppos_output_append(pppos_pcb *pppos, err_t err, struct pbuf *nb, u8_t c, u8_t accm, u16_t *fcs)
{
if (err != ERR_OK) {
return err;
}
/* Make sure there is room for the character and an escape code.
* Sure we don't quite fill the buffer if the character doesn't
* get escaped but is one character worth complicating this? */
if ((PBUF_POOL_BUFSIZE - nb->len) < 2) {
u32_t l = pppos->output_cb(pppos->ppp, (u8_t*)nb->payload, nb->len, pppos->ppp->ctx_cb);
if (l != nb->len) {
return ERR_IF;
}
nb->len = 0;
}
/* Update FCS before checking for special characters. */
if (fcs) {
*fcs = PPP_FCS(*fcs, c);
}
/* Copy to output buffer escaping special characters. */
if (accm && ESCAPE_P(pppos->out_accm, c)) {
*((u8_t*)nb->payload + nb->len++) = PPP_ESCAPE;
*((u8_t*)nb->payload + nb->len++) = c ^ PPP_TRANS;
} else {
*((u8_t*)nb->payload + nb->len++) = c;
}
return ERR_OK;
}
static err_t
pppos_output_last(pppos_pcb *pppos, err_t err, struct pbuf *nb, u16_t *fcs)
{
ppp_pcb *ppp = pppos->ppp;
/* Add FCS and trailing flag. */
err = pppos_output_append(pppos, err, nb, ~(*fcs) & 0xFF, 1, NULL);
err = pppos_output_append(pppos, err, nb, (~(*fcs) >> 8) & 0xFF, 1, NULL);
err = pppos_output_append(pppos, err, nb, PPP_FLAG, 0, NULL);
if (err != ERR_OK) {
goto failed;
}
/* Send remaining buffer if not empty */
if (nb->len > 0) {
u32_t l = pppos->output_cb(ppp, (u8_t*)nb->payload, nb->len, ppp->ctx_cb);
if (l != nb->len) {
err = ERR_IF;
goto failed;
}
}
pppos->last_xmit = sys_now();
MIB2_STATS_NETIF_ADD(ppp->netif, ifoutoctets, nb->tot_len);
MIB2_STATS_NETIF_INC(ppp->netif, ifoutucastpkts);
LINK_STATS_INC(link.xmit);
pbuf_free(nb);
return ERR_OK;
failed:
pppos->last_xmit = 0; /* prepend PPP_FLAG to next packet */
LINK_STATS_INC(link.err);
LINK_STATS_INC(link.drop);
MIB2_STATS_NETIF_INC(ppp->netif, ifoutdiscards);
pbuf_free(nb);
return err;
}
#endif /* PPP_SUPPORT && PPPOS_SUPPORT */

View File

@@ -0,0 +1,677 @@
/*
* upap.c - User/Password Authentication Protocol.
*
* Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT && PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
/*
* @todo:
*/
#if 0 /* UNUSED */
#include <stdio.h>
#include <string.h>
#endif /* UNUSED */
#include "netif/ppp/ppp_impl.h"
#include "netif/ppp/upap.h"
#if PPP_OPTIONS
/*
* Command-line options.
*/
static option_t pap_option_list[] = {
{ "hide-password", o_bool, &hide_password,
"Don't output passwords to log", OPT_PRIO | 1 },
{ "show-password", o_bool, &hide_password,
"Show password string in debug log messages", OPT_PRIOSUB | 0 },
{ "pap-restart", o_int, &upap[0].us_timeouttime,
"Set retransmit timeout for PAP", OPT_PRIO },
{ "pap-max-authreq", o_int, &upap[0].us_maxtransmits,
"Set max number of transmissions for auth-reqs", OPT_PRIO },
{ "pap-timeout", o_int, &upap[0].us_reqtimeout,
"Set time limit for peer PAP authentication", OPT_PRIO },
{ NULL }
};
#endif /* PPP_OPTIONS */
/*
* Protocol entry points.
*/
static void upap_init(ppp_pcb *pcb);
static void upap_lowerup(ppp_pcb *pcb);
static void upap_lowerdown(ppp_pcb *pcb);
static void upap_input(ppp_pcb *pcb, u_char *inpacket, int l);
static void upap_protrej(ppp_pcb *pcb);
#if PRINTPKT_SUPPORT
static int upap_printpkt(const u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg);
#endif /* PRINTPKT_SUPPORT */
const struct protent pap_protent = {
PPP_PAP,
upap_init,
upap_input,
upap_protrej,
upap_lowerup,
upap_lowerdown,
NULL,
NULL,
#if PRINTPKT_SUPPORT
upap_printpkt,
#endif /* PRINTPKT_SUPPORT */
#if PPP_DATAINPUT
NULL,
#endif /* PPP_DATAINPUT */
#if PRINTPKT_SUPPORT
"PAP",
NULL,
#endif /* PRINTPKT_SUPPORT */
#if PPP_OPTIONS
pap_option_list,
NULL,
#endif /* PPP_OPTIONS */
#if DEMAND_SUPPORT
NULL,
NULL
#endif /* DEMAND_SUPPORT */
};
static void upap_timeout(void *arg);
#if PPP_SERVER
static void upap_reqtimeout(void *arg);
static void upap_rauthreq(ppp_pcb *pcb, u_char *inp, int id, int len);
#endif /* PPP_SERVER */
static void upap_rauthack(ppp_pcb *pcb, u_char *inp, int id, int len);
static void upap_rauthnak(ppp_pcb *pcb, u_char *inp, int id, int len);
static void upap_sauthreq(ppp_pcb *pcb);
#if PPP_SERVER
static void upap_sresp(ppp_pcb *pcb, u_char code, u_char id, const char *msg, int msglen);
#endif /* PPP_SERVER */
/*
* upap_init - Initialize a UPAP unit.
*/
static void upap_init(ppp_pcb *pcb) {
pcb->upap.us_user = NULL;
pcb->upap.us_userlen = 0;
pcb->upap.us_passwd = NULL;
pcb->upap.us_passwdlen = 0;
pcb->upap.us_clientstate = UPAPCS_INITIAL;
#if PPP_SERVER
pcb->upap.us_serverstate = UPAPSS_INITIAL;
#endif /* PPP_SERVER */
pcb->upap.us_id = 0;
}
/*
* upap_authwithpeer - Authenticate us with our peer (start client).
*
* Set new state and send authenticate's.
*/
void upap_authwithpeer(ppp_pcb *pcb, const char *user, const char *password) {
if(!user || !password)
return;
/* Save the username and password we're given */
pcb->upap.us_user = user;
pcb->upap.us_userlen = (u8_t)LWIP_MIN(strlen(user), 0xff);
pcb->upap.us_passwd = password;
pcb->upap.us_passwdlen = (u8_t)LWIP_MIN(strlen(password), 0xff);
pcb->upap.us_transmits = 0;
/* Lower layer up yet? */
if (pcb->upap.us_clientstate == UPAPCS_INITIAL ||
pcb->upap.us_clientstate == UPAPCS_PENDING) {
pcb->upap.us_clientstate = UPAPCS_PENDING;
return;
}
upap_sauthreq(pcb); /* Start protocol */
}
#if PPP_SERVER
/*
* upap_authpeer - Authenticate our peer (start server).
*
* Set new state.
*/
void upap_authpeer(ppp_pcb *pcb) {
/* Lower layer up yet? */
if (pcb->upap.us_serverstate == UPAPSS_INITIAL ||
pcb->upap.us_serverstate == UPAPSS_PENDING) {
pcb->upap.us_serverstate = UPAPSS_PENDING;
return;
}
pcb->upap.us_serverstate = UPAPSS_LISTEN;
if (pcb->settings.pap_req_timeout > 0)
TIMEOUT(upap_reqtimeout, pcb, pcb->settings.pap_req_timeout);
}
#endif /* PPP_SERVER */
/*
* upap_timeout - Retransmission timer for sending auth-reqs expired.
*/
static void upap_timeout(void *arg) {
ppp_pcb *pcb = (ppp_pcb*)arg;
if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ)
return;
if (pcb->upap.us_transmits >= pcb->settings.pap_max_transmits) {
/* give up in disgust */
ppp_error("No response to PAP authenticate-requests");
pcb->upap.us_clientstate = UPAPCS_BADAUTH;
auth_withpeer_fail(pcb, PPP_PAP);
return;
}
upap_sauthreq(pcb); /* Send Authenticate-Request */
}
#if PPP_SERVER
/*
* upap_reqtimeout - Give up waiting for the peer to send an auth-req.
*/
static void upap_reqtimeout(void *arg) {
ppp_pcb *pcb = (ppp_pcb*)arg;
if (pcb->upap.us_serverstate != UPAPSS_LISTEN)
return; /* huh?? */
auth_peer_fail(pcb, PPP_PAP);
pcb->upap.us_serverstate = UPAPSS_BADAUTH;
}
#endif /* PPP_SERVER */
/*
* upap_lowerup - The lower layer is up.
*
* Start authenticating if pending.
*/
static void upap_lowerup(ppp_pcb *pcb) {
if (pcb->upap.us_clientstate == UPAPCS_INITIAL)
pcb->upap.us_clientstate = UPAPCS_CLOSED;
else if (pcb->upap.us_clientstate == UPAPCS_PENDING) {
upap_sauthreq(pcb); /* send an auth-request */
}
#if PPP_SERVER
if (pcb->upap.us_serverstate == UPAPSS_INITIAL)
pcb->upap.us_serverstate = UPAPSS_CLOSED;
else if (pcb->upap.us_serverstate == UPAPSS_PENDING) {
pcb->upap.us_serverstate = UPAPSS_LISTEN;
if (pcb->settings.pap_req_timeout > 0)
TIMEOUT(upap_reqtimeout, pcb, pcb->settings.pap_req_timeout);
}
#endif /* PPP_SERVER */
}
/*
* upap_lowerdown - The lower layer is down.
*
* Cancel all timeouts.
*/
static void upap_lowerdown(ppp_pcb *pcb) {
if (pcb->upap.us_clientstate == UPAPCS_AUTHREQ) /* Timeout pending? */
UNTIMEOUT(upap_timeout, pcb); /* Cancel timeout */
#if PPP_SERVER
if (pcb->upap.us_serverstate == UPAPSS_LISTEN && pcb->settings.pap_req_timeout > 0)
UNTIMEOUT(upap_reqtimeout, pcb);
#endif /* PPP_SERVER */
pcb->upap.us_clientstate = UPAPCS_INITIAL;
#if PPP_SERVER
pcb->upap.us_serverstate = UPAPSS_INITIAL;
#endif /* PPP_SERVER */
}
/*
* upap_protrej - Peer doesn't speak this protocol.
*
* This shouldn't happen. In any case, pretend lower layer went down.
*/
static void upap_protrej(ppp_pcb *pcb) {
if (pcb->upap.us_clientstate == UPAPCS_AUTHREQ) {
ppp_error("PAP authentication failed due to protocol-reject");
auth_withpeer_fail(pcb, PPP_PAP);
}
#if PPP_SERVER
if (pcb->upap.us_serverstate == UPAPSS_LISTEN) {
ppp_error("PAP authentication of peer failed (protocol-reject)");
auth_peer_fail(pcb, PPP_PAP);
}
#endif /* PPP_SERVER */
upap_lowerdown(pcb);
}
/*
* upap_input - Input UPAP packet.
*/
static void upap_input(ppp_pcb *pcb, u_char *inpacket, int l) {
u_char *inp;
u_char code, id;
int len;
/*
* Parse header (code, id and length).
* If packet too short, drop it.
*/
inp = inpacket;
if (l < UPAP_HEADERLEN) {
UPAPDEBUG(("pap_input: rcvd short header."));
return;
}
GETCHAR(code, inp);
GETCHAR(id, inp);
GETSHORT(len, inp);
if (len < UPAP_HEADERLEN) {
UPAPDEBUG(("pap_input: rcvd illegal length."));
return;
}
if (len > l) {
UPAPDEBUG(("pap_input: rcvd short packet."));
return;
}
len -= UPAP_HEADERLEN;
/*
* Action depends on code.
*/
switch (code) {
case UPAP_AUTHREQ:
#if PPP_SERVER
upap_rauthreq(pcb, inp, id, len);
#endif /* PPP_SERVER */
break;
case UPAP_AUTHACK:
upap_rauthack(pcb, inp, id, len);
break;
case UPAP_AUTHNAK:
upap_rauthnak(pcb, inp, id, len);
break;
default: /* XXX Need code reject */
break;
}
}
#if PPP_SERVER
/*
* upap_rauth - Receive Authenticate.
*/
static void upap_rauthreq(ppp_pcb *pcb, u_char *inp, int id, int len) {
u_char ruserlen, rpasswdlen;
char *ruser;
char *rpasswd;
char rhostname[256];
int retcode;
const char *msg;
int msglen;
if (pcb->upap.us_serverstate < UPAPSS_LISTEN)
return;
/*
* If we receive a duplicate authenticate-request, we are
* supposed to return the same status as for the first request.
*/
if (pcb->upap.us_serverstate == UPAPSS_OPEN) {
upap_sresp(pcb, UPAP_AUTHACK, id, "", 0); /* return auth-ack */
return;
}
if (pcb->upap.us_serverstate == UPAPSS_BADAUTH) {
upap_sresp(pcb, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */
return;
}
/*
* Parse user/passwd.
*/
if (len < 1) {
UPAPDEBUG(("pap_rauth: rcvd short packet."));
return;
}
GETCHAR(ruserlen, inp);
len -= sizeof (u_char) + ruserlen + sizeof (u_char);
if (len < 0) {
UPAPDEBUG(("pap_rauth: rcvd short packet."));
return;
}
ruser = (char *) inp;
INCPTR(ruserlen, inp);
GETCHAR(rpasswdlen, inp);
if (len < rpasswdlen) {
UPAPDEBUG(("pap_rauth: rcvd short packet."));
return;
}
rpasswd = (char *) inp;
/*
* Check the username and password given.
*/
retcode = UPAP_AUTHNAK;
if (auth_check_passwd(pcb, ruser, ruserlen, rpasswd, rpasswdlen, &msg, &msglen)) {
retcode = UPAP_AUTHACK;
}
BZERO(rpasswd, rpasswdlen);
#if 0 /* UNUSED */
/*
* Check remote number authorization. A plugin may have filled in
* the remote number or added an allowed number, and rather than
* return an authenticate failure, is leaving it for us to verify.
*/
if (retcode == UPAP_AUTHACK) {
if (!auth_number()) {
/* We do not want to leak info about the pap result. */
retcode = UPAP_AUTHNAK; /* XXX exit value will be "wrong" */
warn("calling number %q is not authorized", remote_number);
}
}
msglen = strlen(msg);
if (msglen > 255)
msglen = 255;
#endif /* UNUSED */
upap_sresp(pcb, retcode, id, msg, msglen);
/* Null terminate and clean remote name. */
ppp_slprintf(rhostname, sizeof(rhostname), "%.*v", ruserlen, ruser);
if (retcode == UPAP_AUTHACK) {
pcb->upap.us_serverstate = UPAPSS_OPEN;
ppp_notice("PAP peer authentication succeeded for %q", rhostname);
auth_peer_success(pcb, PPP_PAP, 0, ruser, ruserlen);
} else {
pcb->upap.us_serverstate = UPAPSS_BADAUTH;
ppp_warn("PAP peer authentication failed for %q", rhostname);
auth_peer_fail(pcb, PPP_PAP);
}
if (pcb->settings.pap_req_timeout > 0)
UNTIMEOUT(upap_reqtimeout, pcb);
}
#endif /* PPP_SERVER */
/*
* upap_rauthack - Receive Authenticate-Ack.
*/
static void upap_rauthack(ppp_pcb *pcb, u_char *inp, int id, int len) {
u_char msglen;
char *msg;
LWIP_UNUSED_ARG(id);
if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ) /* XXX */
return;
/*
* Parse message.
*/
if (len < 1) {
UPAPDEBUG(("pap_rauthack: ignoring missing msg-length."));
} else {
GETCHAR(msglen, inp);
if (msglen > 0) {
len -= sizeof (u_char);
if (len < msglen) {
UPAPDEBUG(("pap_rauthack: rcvd short packet."));
return;
}
msg = (char *) inp;
PRINTMSG(msg, msglen);
}
}
pcb->upap.us_clientstate = UPAPCS_OPEN;
auth_withpeer_success(pcb, PPP_PAP, 0);
}
/*
* upap_rauthnak - Receive Authenticate-Nak.
*/
static void upap_rauthnak(ppp_pcb *pcb, u_char *inp, int id, int len) {
u_char msglen;
char *msg;
LWIP_UNUSED_ARG(id);
if (pcb->upap.us_clientstate != UPAPCS_AUTHREQ) /* XXX */
return;
/*
* Parse message.
*/
if (len < 1) {
UPAPDEBUG(("pap_rauthnak: ignoring missing msg-length."));
} else {
GETCHAR(msglen, inp);
if (msglen > 0) {
len -= sizeof (u_char);
if (len < msglen) {
UPAPDEBUG(("pap_rauthnak: rcvd short packet."));
return;
}
msg = (char *) inp;
PRINTMSG(msg, msglen);
}
}
pcb->upap.us_clientstate = UPAPCS_BADAUTH;
ppp_error("PAP authentication failed");
auth_withpeer_fail(pcb, PPP_PAP);
}
/*
* upap_sauthreq - Send an Authenticate-Request.
*/
static void upap_sauthreq(ppp_pcb *pcb) {
struct pbuf *p;
u_char *outp;
int outlen;
outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) +
pcb->upap.us_userlen + pcb->upap.us_passwdlen;
p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN +outlen), PPP_CTRL_PBUF_TYPE);
if(NULL == p)
return;
if(p->tot_len != p->len) {
pbuf_free(p);
return;
}
outp = (u_char*)p->payload;
MAKEHEADER(outp, PPP_PAP);
PUTCHAR(UPAP_AUTHREQ, outp);
PUTCHAR(++pcb->upap.us_id, outp);
PUTSHORT(outlen, outp);
PUTCHAR(pcb->upap.us_userlen, outp);
MEMCPY(outp, pcb->upap.us_user, pcb->upap.us_userlen);
INCPTR(pcb->upap.us_userlen, outp);
PUTCHAR(pcb->upap.us_passwdlen, outp);
MEMCPY(outp, pcb->upap.us_passwd, pcb->upap.us_passwdlen);
ppp_write(pcb, p);
TIMEOUT(upap_timeout, pcb, pcb->settings.pap_timeout_time);
++pcb->upap.us_transmits;
pcb->upap.us_clientstate = UPAPCS_AUTHREQ;
}
#if PPP_SERVER
/*
* upap_sresp - Send a response (ack or nak).
*/
static void upap_sresp(ppp_pcb *pcb, u_char code, u_char id, const char *msg, int msglen) {
struct pbuf *p;
u_char *outp;
int outlen;
outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen;
p = pbuf_alloc(PBUF_RAW, (u16_t)(PPP_HDRLEN +outlen), PPP_CTRL_PBUF_TYPE);
if(NULL == p)
return;
if(p->tot_len != p->len) {
pbuf_free(p);
return;
}
outp = (u_char*)p->payload;
MAKEHEADER(outp, PPP_PAP);
PUTCHAR(code, outp);
PUTCHAR(id, outp);
PUTSHORT(outlen, outp);
PUTCHAR(msglen, outp);
MEMCPY(outp, msg, msglen);
ppp_write(pcb, p);
}
#endif /* PPP_SERVER */
#if PRINTPKT_SUPPORT
/*
* upap_printpkt - print the contents of a PAP packet.
*/
static const char* const upap_codenames[] = {
"AuthReq", "AuthAck", "AuthNak"
};
static int upap_printpkt(const u_char *p, int plen, void (*printer) (void *, const char *, ...), void *arg) {
int code, id, len;
int mlen, ulen, wlen;
const u_char *user, *pwd, *msg;
const u_char *pstart;
if (plen < UPAP_HEADERLEN)
return 0;
pstart = p;
GETCHAR(code, p);
GETCHAR(id, p);
GETSHORT(len, p);
if (len < UPAP_HEADERLEN || len > plen)
return 0;
if (code >= 1 && code <= (int)LWIP_ARRAYSIZE(upap_codenames))
printer(arg, " %s", upap_codenames[code-1]);
else
printer(arg, " code=0x%x", code);
printer(arg, " id=0x%x", id);
len -= UPAP_HEADERLEN;
switch (code) {
case UPAP_AUTHREQ:
if (len < 1)
break;
ulen = p[0];
if (len < ulen + 2)
break;
wlen = p[ulen + 1];
if (len < ulen + wlen + 2)
break;
user = (const u_char *) (p + 1);
pwd = (const u_char *) (p + ulen + 2);
p += ulen + wlen + 2;
len -= ulen + wlen + 2;
printer(arg, " user=");
ppp_print_string(user, ulen, printer, arg);
printer(arg, " password=");
/* FIXME: require ppp_pcb struct as printpkt() argument */
#if 0
if (!pcb->settings.hide_password)
#endif
ppp_print_string(pwd, wlen, printer, arg);
#if 0
else
printer(arg, "<hidden>");
#endif
break;
case UPAP_AUTHACK:
case UPAP_AUTHNAK:
if (len < 1)
break;
mlen = p[0];
if (len < mlen + 1)
break;
msg = (const u_char *) (p + 1);
p += mlen + 1;
len -= mlen + 1;
printer(arg, " ");
ppp_print_string(msg, mlen, printer, arg);
break;
default:
break;
}
/* print the rest of the bytes in the packet */
for (; len > 0; --len) {
GETCHAR(code, p);
printer(arg, " %.2x", code);
}
return p - pstart;
}
#endif /* PRINTPKT_SUPPORT */
#endif /* PPP_SUPPORT && PAP_SUPPORT */

View File

@@ -0,0 +1,957 @@
/*
* utils.c - various utility functions used in pppd.
*
* Copyright (c) 1999-2002 Paul Mackerras. 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. The name(s) of the authors of this software must not be used to
* endorse or promote products derived from this software without
* prior written permission.
*
* 3. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Paul Mackerras
* <paulus@samba.org>".
*
* THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
#if 0 /* UNUSED */
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <syslog.h>
#include <netdb.h>
#include <time.h>
#include <utmp.h>
#include <pwd.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <netinet/in.h>
#ifdef SVR4
#include <sys/mkdev.h>
#endif
#endif /* UNUSED */
#include "netif/ppp/ppp_impl.h"
#include "netif/ppp/fsm.h"
#include "netif/ppp/lcp.h"
#if defined(SUNOS4)
extern char *strerror();
#endif
static void ppp_logit(int level, const char *fmt, va_list args);
static void ppp_log_write(int level, char *buf);
#if PRINTPKT_SUPPORT
static void ppp_vslp_printer(void *arg, const char *fmt, ...);
static void ppp_format_packet(const u_char *p, int len,
void (*printer) (void *, const char *, ...), void *arg);
struct buffer_info {
char *ptr;
int len;
};
#endif /* PRINTPKT_SUPPORT */
/*
* ppp_strlcpy - like strcpy/strncpy, doesn't overflow destination buffer,
* always leaves destination null-terminated (for len > 0).
*/
size_t ppp_strlcpy(char *dest, const char *src, size_t len) {
size_t ret = strlen(src);
if (len != 0) {
if (ret < len)
strcpy(dest, src);
else {
strncpy(dest, src, len - 1);
dest[len-1] = 0;
}
}
return ret;
}
/*
* ppp_strlcat - like strcat/strncat, doesn't overflow destination buffer,
* always leaves destination null-terminated (for len > 0).
*/
size_t ppp_strlcat(char *dest, const char *src, size_t len) {
size_t dlen = strlen(dest);
return dlen + ppp_strlcpy(dest + dlen, src, (len > dlen? len - dlen: 0));
}
/*
* ppp_slprintf - format a message into a buffer. Like sprintf except we
* also specify the length of the output buffer, and we handle
* %m (error message), %v (visible string),
* %q (quoted string), %t (current time) and %I (IP address) formats.
* Doesn't do floating-point formats.
* Returns the number of chars put into buf.
*/
int ppp_slprintf(char *buf, int buflen, const char *fmt, ...) {
va_list args;
int n;
va_start(args, fmt);
n = ppp_vslprintf(buf, buflen, fmt, args);
va_end(args);
return n;
}
/*
* ppp_vslprintf - like ppp_slprintf, takes a va_list instead of a list of args.
*/
#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0)
int ppp_vslprintf(char *buf, int buflen, const char *fmt, va_list args) {
int c, i, n;
int width, prec, fillch;
int base, len, neg, quoted;
unsigned long val = 0;
const char *f;
char *str, *buf0;
const unsigned char *p;
char num[32];
#if 0 /* need port */
time_t t;
#endif /* need port */
u32_t ip;
static char hexchars[] = "0123456789abcdef";
#if PRINTPKT_SUPPORT
struct buffer_info bufinfo;
#endif /* PRINTPKT_SUPPORT */
buf0 = buf;
--buflen;
while (buflen > 0) {
for (f = fmt; *f != '%' && *f != 0; ++f)
;
if (f > fmt) {
len = f - fmt;
if (len > buflen)
len = buflen;
memcpy(buf, fmt, len);
buf += len;
buflen -= len;
fmt = f;
}
if (*fmt == 0)
break;
c = *++fmt;
width = 0;
prec = -1;
fillch = ' ';
if (c == '0') {
fillch = '0';
c = *++fmt;
}
if (c == '*') {
width = va_arg(args, int);
c = *++fmt;
} else {
while (lwip_isdigit(c)) {
width = width * 10 + c - '0';
c = *++fmt;
}
}
if (c == '.') {
c = *++fmt;
if (c == '*') {
prec = va_arg(args, int);
c = *++fmt;
} else {
prec = 0;
while (lwip_isdigit(c)) {
prec = prec * 10 + c - '0';
c = *++fmt;
}
}
}
str = 0;
base = 0;
neg = 0;
++fmt;
switch (c) {
case 'l':
c = *fmt++;
switch (c) {
case 'd':
val = va_arg(args, long);
if ((long)val < 0) {
neg = 1;
val = (unsigned long)-(long)val;
}
base = 10;
break;
case 'u':
val = va_arg(args, unsigned long);
base = 10;
break;
default:
OUTCHAR('%');
OUTCHAR('l');
--fmt; /* so %lz outputs %lz etc. */
continue;
}
break;
case 'd':
i = va_arg(args, int);
if (i < 0) {
neg = 1;
val = -i;
} else
val = i;
base = 10;
break;
case 'u':
val = va_arg(args, unsigned int);
base = 10;
break;
case 'o':
val = va_arg(args, unsigned int);
base = 8;
break;
case 'x':
case 'X':
val = va_arg(args, unsigned int);
base = 16;
break;
#if 0 /* unused (and wrong on LLP64 systems) */
case 'p':
val = (unsigned long) va_arg(args, void *);
base = 16;
neg = 2;
break;
#endif /* unused (and wrong on LLP64 systems) */
case 's':
str = va_arg(args, char *);
break;
case 'c':
num[0] = va_arg(args, int);
num[1] = 0;
str = num;
break;
#if 0 /* do we always have strerror() in embedded ? */
case 'm':
str = strerror(errno);
break;
#endif /* do we always have strerror() in embedded ? */
case 'I':
ip = va_arg(args, u32_t);
ip = lwip_ntohl(ip);
ppp_slprintf(num, sizeof(num), "%d.%d.%d.%d", (ip >> 24) & 0xff,
(ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff);
str = num;
break;
#if 0 /* need port */
case 't':
time(&t);
str = ctime(&t);
str += 4; /* chop off the day name */
str[15] = 0; /* chop off year and newline */
break;
#endif /* need port */
case 'v': /* "visible" string */
case 'q': /* quoted string */
quoted = c == 'q';
p = va_arg(args, unsigned char *);
if (p == NULL)
p = (const unsigned char *)"<NULL>";
if (fillch == '0' && prec >= 0) {
n = prec;
} else {
n = strlen((const char *)p);
if (prec >= 0 && n > prec)
n = prec;
}
while (n > 0 && buflen > 0) {
c = *p++;
--n;
if (!quoted && c >= 0x80) {
OUTCHAR('M');
OUTCHAR('-');
c -= 0x80;
}
if (quoted && (c == '"' || c == '\\'))
OUTCHAR('\\');
if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
if (quoted) {
OUTCHAR('\\');
switch (c) {
case '\t': OUTCHAR('t'); break;
case '\n': OUTCHAR('n'); break;
case '\b': OUTCHAR('b'); break;
case '\f': OUTCHAR('f'); break;
default:
OUTCHAR('x');
OUTCHAR(hexchars[c >> 4]);
OUTCHAR(hexchars[c & 0xf]);
}
} else {
if (c == '\t')
OUTCHAR(c);
else {
OUTCHAR('^');
OUTCHAR(c ^ 0x40);
}
}
} else
OUTCHAR(c);
}
continue;
#if PRINTPKT_SUPPORT
case 'P': /* print PPP packet */
bufinfo.ptr = buf;
bufinfo.len = buflen + 1;
p = va_arg(args, unsigned char *);
n = va_arg(args, int);
ppp_format_packet(p, n, ppp_vslp_printer, &bufinfo);
buf = bufinfo.ptr;
buflen = bufinfo.len - 1;
continue;
#endif /* PRINTPKT_SUPPORT */
case 'B':
p = va_arg(args, unsigned char *);
for (n = prec; n > 0; --n) {
c = *p++;
if (fillch == ' ')
OUTCHAR(' ');
OUTCHAR(hexchars[(c >> 4) & 0xf]);
OUTCHAR(hexchars[c & 0xf]);
}
continue;
default:
*buf++ = '%';
if (c != '%')
--fmt; /* so %z outputs %z etc. */
--buflen;
continue;
}
if (base != 0) {
str = num + sizeof(num);
*--str = 0;
while (str > num + neg) {
*--str = hexchars[val % base];
val = val / base;
if (--prec <= 0 && val == 0)
break;
}
switch (neg) {
case 1:
*--str = '-';
break;
case 2:
*--str = 'x';
*--str = '0';
break;
default:
break;
}
len = num + sizeof(num) - 1 - str;
} else {
len = strlen(str);
if (prec >= 0 && len > prec)
len = prec;
}
if (width > 0) {
if (width > buflen)
width = buflen;
if ((n = width - len) > 0) {
buflen -= n;
for (; n > 0; --n)
*buf++ = fillch;
}
}
if (len > buflen)
len = buflen;
memcpy(buf, str, len);
buf += len;
buflen -= len;
}
*buf = 0;
return buf - buf0;
}
#if PRINTPKT_SUPPORT
/*
* vslp_printer - used in processing a %P format
*/
static void ppp_vslp_printer(void *arg, const char *fmt, ...) {
int n;
va_list pvar;
struct buffer_info *bi;
va_start(pvar, fmt);
bi = (struct buffer_info *) arg;
n = ppp_vslprintf(bi->ptr, bi->len, fmt, pvar);
va_end(pvar);
bi->ptr += n;
bi->len -= n;
}
#endif /* PRINTPKT_SUPPORT */
#if 0 /* UNUSED */
/*
* log_packet - format a packet and log it.
*/
void
log_packet(p, len, prefix, level)
u_char *p;
int len;
char *prefix;
int level;
{
init_pr_log(prefix, level);
ppp_format_packet(p, len, pr_log, &level);
end_pr_log();
}
#endif /* UNUSED */
#if PRINTPKT_SUPPORT
/*
* ppp_format_packet - make a readable representation of a packet,
* calling `printer(arg, format, ...)' to output it.
*/
static void ppp_format_packet(const u_char *p, int len,
void (*printer) (void *, const char *, ...), void *arg) {
int i, n;
u_short proto;
const struct protent *protp;
if (len >= 2) {
GETSHORT(proto, p);
len -= 2;
for (i = 0; (protp = protocols[i]) != NULL; ++i)
if (proto == protp->protocol)
break;
if (protp != NULL) {
printer(arg, "[%s", protp->name);
n = (*protp->printpkt)(p, len, printer, arg);
printer(arg, "]");
p += n;
len -= n;
} else {
for (i = 0; (protp = protocols[i]) != NULL; ++i)
if (proto == (protp->protocol & ~0x8000))
break;
if (protp != 0 && protp->data_name != 0) {
printer(arg, "[%s data]", protp->data_name);
if (len > 8)
printer(arg, "%.8B ...", p);
else
printer(arg, "%.*B", len, p);
len = 0;
} else
printer(arg, "[proto=0x%x]", proto);
}
}
if (len > 32)
printer(arg, "%.32B ...", p);
else
printer(arg, "%.*B", len, p);
}
#endif /* PRINTPKT_SUPPORT */
#if 0 /* UNUSED */
/*
* init_pr_log, end_pr_log - initialize and finish use of pr_log.
*/
static char line[256]; /* line to be logged accumulated here */
static char *linep; /* current pointer within line */
static int llevel; /* level for logging */
void
init_pr_log(prefix, level)
const char *prefix;
int level;
{
linep = line;
if (prefix != NULL) {
ppp_strlcpy(line, prefix, sizeof(line));
linep = line + strlen(line);
}
llevel = level;
}
void
end_pr_log()
{
if (linep != line) {
*linep = 0;
ppp_log_write(llevel, line);
}
}
/*
* pr_log - printer routine for outputting to log
*/
void
pr_log (void *arg, const char *fmt, ...)
{
int l, n;
va_list pvar;
char *p, *eol;
char buf[256];
va_start(pvar, fmt);
n = ppp_vslprintf(buf, sizeof(buf), fmt, pvar);
va_end(pvar);
p = buf;
eol = strchr(buf, '\n');
if (linep != line) {
l = (eol == NULL)? n: eol - buf;
if (linep + l < line + sizeof(line)) {
if (l > 0) {
memcpy(linep, buf, l);
linep += l;
}
if (eol == NULL)
return;
p = eol + 1;
eol = strchr(p, '\n');
}
*linep = 0;
ppp_log_write(llevel, line);
linep = line;
}
while (eol != NULL) {
*eol = 0;
ppp_log_write(llevel, p);
p = eol + 1;
eol = strchr(p, '\n');
}
/* assumes sizeof(buf) <= sizeof(line) */
l = buf + n - p;
if (l > 0) {
memcpy(line, p, n);
linep = line + l;
}
}
#endif /* UNUSED */
/*
* ppp_print_string - print a readable representation of a string using
* printer.
*/
void ppp_print_string(const u_char *p, int len, void (*printer) (void *, const char *, ...), void *arg) {
int c;
printer(arg, "\"");
for (; len > 0; --len) {
c = *p++;
if (' ' <= c && c <= '~') {
if (c == '\\' || c == '"')
printer(arg, "\\");
printer(arg, "%c", c);
} else {
switch (c) {
case '\n':
printer(arg, "\\n");
break;
case '\r':
printer(arg, "\\r");
break;
case '\t':
printer(arg, "\\t");
break;
default:
printer(arg, "\\%.3o", (u8_t)c);
/* no break */
}
}
}
printer(arg, "\"");
}
/*
* ppp_logit - does the hard work for fatal et al.
*/
static void ppp_logit(int level, const char *fmt, va_list args) {
char buf[1024];
ppp_vslprintf(buf, sizeof(buf), fmt, args);
ppp_log_write(level, buf);
}
static void ppp_log_write(int level, char *buf) {
LWIP_UNUSED_ARG(level); /* necessary if PPPDEBUG is defined to an empty function */
LWIP_UNUSED_ARG(buf);
PPPDEBUG(level, ("%s\n", buf) );
#if 0
if (log_to_fd >= 0 && (level != LOG_DEBUG || debug)) {
int n = strlen(buf);
if (n > 0 && buf[n-1] == '\n')
--n;
if (write(log_to_fd, buf, n) != n
|| write(log_to_fd, "\n", 1) != 1)
log_to_fd = -1;
}
#endif
}
/*
* ppp_fatal - log an error message and die horribly.
*/
void ppp_fatal(const char *fmt, ...) {
va_list pvar;
va_start(pvar, fmt);
ppp_logit(LOG_ERR, fmt, pvar);
va_end(pvar);
LWIP_ASSERT("ppp_fatal", 0); /* as promised */
}
/*
* ppp_error - log an error message.
*/
void ppp_error(const char *fmt, ...) {
va_list pvar;
va_start(pvar, fmt);
ppp_logit(LOG_ERR, fmt, pvar);
va_end(pvar);
#if 0 /* UNUSED */
++error_count;
#endif /* UNUSED */
}
/*
* ppp_warn - log a warning message.
*/
void ppp_warn(const char *fmt, ...) {
va_list pvar;
va_start(pvar, fmt);
ppp_logit(LOG_WARNING, fmt, pvar);
va_end(pvar);
}
/*
* ppp_notice - log a notice-level message.
*/
void ppp_notice(const char *fmt, ...) {
va_list pvar;
va_start(pvar, fmt);
ppp_logit(LOG_NOTICE, fmt, pvar);
va_end(pvar);
}
/*
* ppp_info - log an informational message.
*/
void ppp_info(const char *fmt, ...) {
va_list pvar;
va_start(pvar, fmt);
ppp_logit(LOG_INFO, fmt, pvar);
va_end(pvar);
}
/*
* ppp_dbglog - log a debug message.
*/
void ppp_dbglog(const char *fmt, ...) {
va_list pvar;
va_start(pvar, fmt);
ppp_logit(LOG_DEBUG, fmt, pvar);
va_end(pvar);
}
#if PRINTPKT_SUPPORT
/*
* ppp_dump_packet - print out a packet in readable form if it is interesting.
* Assumes len >= PPP_HDRLEN.
*/
void ppp_dump_packet(ppp_pcb *pcb, const char *tag, unsigned char *p, int len) {
int proto;
/*
* don't print data packets, i.e. IPv4, IPv6, VJ, and compressed packets.
*/
proto = (p[0] << 8) + p[1];
if (proto < 0xC000 && (proto & ~0x8000) == proto)
return;
/*
* don't print valid LCP echo request/reply packets if the link is up.
*/
if (proto == PPP_LCP && pcb->phase == PPP_PHASE_RUNNING && len >= 2 + HEADERLEN) {
unsigned char *lcp = p + 2;
int l = (lcp[2] << 8) + lcp[3];
if ((lcp[0] == ECHOREQ || lcp[0] == ECHOREP)
&& l >= HEADERLEN && l <= len - 2)
return;
}
ppp_dbglog("%s %P", tag, p, len);
}
#endif /* PRINTPKT_SUPPORT */
#if 0 /* Unused */
/*
* complete_read - read a full `count' bytes from fd,
* unless end-of-file or an error other than EINTR is encountered.
*/
ssize_t
complete_read(int fd, void *buf, size_t count)
{
size_t done;
ssize_t nb;
char *ptr = buf;
for (done = 0; done < count; ) {
nb = read(fd, ptr, count - done);
if (nb < 0) {
if (errno == EINTR)
continue;
return -1;
}
if (nb == 0)
break;
done += nb;
ptr += nb;
}
return done;
}
/* Procedures for locking the serial device using a lock file. */
#ifndef LOCK_DIR
#ifdef __linux__
#define LOCK_DIR "/var/lock"
#else
#ifdef SVR4
#define LOCK_DIR "/var/spool/locks"
#else
#define LOCK_DIR "/var/spool/lock"
#endif
#endif
#endif /* LOCK_DIR */
static char lock_file[MAXPATHLEN];
/*
* lock - create a lock file for the named device
*/
int
lock(dev)
char *dev;
{
#ifdef LOCKLIB
int result;
result = mklock (dev, (void *) 0);
if (result == 0) {
ppp_strlcpy(lock_file, dev, sizeof(lock_file));
return 0;
}
if (result > 0)
ppp_notice("Device %s is locked by pid %d", dev, result);
else
ppp_error("Can't create lock file %s", lock_file);
return -1;
#else /* LOCKLIB */
char lock_buffer[12];
int fd, pid, n;
#ifdef SVR4
struct stat sbuf;
if (stat(dev, &sbuf) < 0) {
ppp_error("Can't get device number for %s: %m", dev);
return -1;
}
if ((sbuf.st_mode & S_IFMT) != S_IFCHR) {
ppp_error("Can't lock %s: not a character device", dev);
return -1;
}
ppp_slprintf(lock_file, sizeof(lock_file), "%s/LK.%03d.%03d.%03d",
LOCK_DIR, major(sbuf.st_dev),
major(sbuf.st_rdev), minor(sbuf.st_rdev));
#else
char *p;
char lockdev[MAXPATHLEN];
if ((p = strstr(dev, "dev/")) != NULL) {
dev = p + 4;
strncpy(lockdev, dev, MAXPATHLEN-1);
lockdev[MAXPATHLEN-1] = 0;
while ((p = strrchr(lockdev, '/')) != NULL) {
*p = '_';
}
dev = lockdev;
} else
if ((p = strrchr(dev, '/')) != NULL)
dev = p + 1;
ppp_slprintf(lock_file, sizeof(lock_file), "%s/LCK..%s", LOCK_DIR, dev);
#endif
while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) {
if (errno != EEXIST) {
ppp_error("Can't create lock file %s: %m", lock_file);
break;
}
/* Read the lock file to find out who has the device locked. */
fd = open(lock_file, O_RDONLY, 0);
if (fd < 0) {
if (errno == ENOENT) /* This is just a timing problem. */
continue;
ppp_error("Can't open existing lock file %s: %m", lock_file);
break;
}
#ifndef LOCK_BINARY
n = read(fd, lock_buffer, 11);
#else
n = read(fd, &pid, sizeof(pid));
#endif /* LOCK_BINARY */
close(fd);
fd = -1;
if (n <= 0) {
ppp_error("Can't read pid from lock file %s", lock_file);
break;
}
/* See if the process still exists. */
#ifndef LOCK_BINARY
lock_buffer[n] = 0;
pid = atoi(lock_buffer);
#endif /* LOCK_BINARY */
if (pid == getpid())
return 1; /* somebody else locked it for us */
if (pid == 0
|| (kill(pid, 0) == -1 && errno == ESRCH)) {
if (unlink (lock_file) == 0) {
ppp_notice("Removed stale lock on %s (pid %d)", dev, pid);
continue;
}
ppp_warn("Couldn't remove stale lock on %s", dev);
} else
ppp_notice("Device %s is locked by pid %d", dev, pid);
break;
}
if (fd < 0) {
lock_file[0] = 0;
return -1;
}
pid = getpid();
#ifndef LOCK_BINARY
ppp_slprintf(lock_buffer, sizeof(lock_buffer), "%10d\n", pid);
write (fd, lock_buffer, 11);
#else
write(fd, &pid, sizeof (pid));
#endif
close(fd);
return 0;
#endif
}
/*
* relock - called to update our lockfile when we are about to detach,
* thus changing our pid (we fork, the child carries on, and the parent dies).
* Note that this is called by the parent, with pid equal to the pid
* of the child. This avoids a potential race which would exist if
* we had the child rewrite the lockfile (the parent might die first,
* and another process could think the lock was stale if it checked
* between when the parent died and the child rewrote the lockfile).
*/
int
relock(pid)
int pid;
{
#ifdef LOCKLIB
/* XXX is there a way to do this? */
return -1;
#else /* LOCKLIB */
int fd;
char lock_buffer[12];
if (lock_file[0] == 0)
return -1;
fd = open(lock_file, O_WRONLY, 0);
if (fd < 0) {
ppp_error("Couldn't reopen lock file %s: %m", lock_file);
lock_file[0] = 0;
return -1;
}
#ifndef LOCK_BINARY
ppp_slprintf(lock_buffer, sizeof(lock_buffer), "%10d\n", pid);
write (fd, lock_buffer, 11);
#else
write(fd, &pid, sizeof(pid));
#endif /* LOCK_BINARY */
close(fd);
return 0;
#endif /* LOCKLIB */
}
/*
* unlock - remove our lockfile
*/
void
unlock()
{
if (lock_file[0]) {
#ifdef LOCKLIB
(void) rmlock(lock_file, (void *) 0);
#else
unlink(lock_file);
#endif
lock_file[0] = 0;
}
}
#endif /* Unused */
#endif /* PPP_SUPPORT */

View File

@@ -0,0 +1,685 @@
/*
* Routines to compress and uncompess tcp packets (for transmission
* over low speed serial lines.
*
* Copyright (c) 1989 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
* Initial distribution.
*
* Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au,
* so that the entire packet being decompressed doesn't have
* to be in contiguous memory (just the compressed header).
*
* Modified March 1998 by Guy Lancaster, glanca@gesn.com,
* for a 16 bit processor.
*/
#include "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT && VJ_SUPPORT /* don't build if not configured for use in lwipopts.h */
#include "netif/ppp/ppp_impl.h"
#include "netif/ppp/pppdebug.h"
#include "netif/ppp/vj.h"
#include <string.h>
#if LINK_STATS
#define INCR(counter) ++comp->stats.counter
#else
#define INCR(counter)
#endif
void
vj_compress_init(struct vjcompress *comp)
{
u8_t i;
struct cstate *tstate = comp->tstate;
#if MAX_SLOTS == 0
memset((char *)comp, 0, sizeof(*comp));
#endif
comp->maxSlotIndex = MAX_SLOTS - 1;
comp->compressSlot = 0; /* Disable slot ID compression by default. */
for (i = MAX_SLOTS - 1; i > 0; --i) {
tstate[i].cs_id = i;
tstate[i].cs_next = &tstate[i - 1];
}
tstate[0].cs_next = &tstate[MAX_SLOTS - 1];
tstate[0].cs_id = 0;
comp->last_cs = &tstate[0];
comp->last_recv = 255;
comp->last_xmit = 255;
comp->flags = VJF_TOSS;
}
/* ENCODE encodes a number that is known to be non-zero. ENCODEZ
* checks for zero (since zero has to be encoded in the long, 3 byte
* form).
*/
#define ENCODE(n) { \
if ((u16_t)(n) >= 256) { \
*cp++ = 0; \
cp[1] = (u8_t)(n); \
cp[0] = (u8_t)((n) >> 8); \
cp += 2; \
} else { \
*cp++ = (u8_t)(n); \
} \
}
#define ENCODEZ(n) { \
if ((u16_t)(n) >= 256 || (u16_t)(n) == 0) { \
*cp++ = 0; \
cp[1] = (u8_t)(n); \
cp[0] = (u8_t)((n) >> 8); \
cp += 2; \
} else { \
*cp++ = (u8_t)(n); \
} \
}
#define DECODEL(f) { \
if (*cp == 0) {\
u32_t tmp_ = lwip_ntohl(f) + ((cp[1] << 8) | cp[2]); \
(f) = lwip_htonl(tmp_); \
cp += 3; \
} else { \
u32_t tmp_ = lwip_ntohl(f) + (u32_t)*cp++; \
(f) = lwip_htonl(tmp_); \
} \
}
#define DECODES(f) { \
if (*cp == 0) {\
u16_t tmp_ = lwip_ntohs(f) + (((u16_t)cp[1] << 8) | cp[2]); \
(f) = lwip_htons(tmp_); \
cp += 3; \
} else { \
u16_t tmp_ = lwip_ntohs(f) + (u16_t)*cp++; \
(f) = lwip_htons(tmp_); \
} \
}
#define DECODEU(f) { \
if (*cp == 0) {\
(f) = lwip_htons(((u16_t)cp[1] << 8) | cp[2]); \
cp += 3; \
} else { \
(f) = lwip_htons((u16_t)*cp++); \
} \
}
/* Helper structures for unaligned *u32_t and *u16_t accesses */
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/bpstruct.h"
#endif
PACK_STRUCT_BEGIN
struct vj_u32_t {
PACK_STRUCT_FIELD(u32_t v);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/epstruct.h"
#endif
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/bpstruct.h"
#endif
PACK_STRUCT_BEGIN
struct vj_u16_t {
PACK_STRUCT_FIELD(u16_t v);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/epstruct.h"
#endif
/*
* vj_compress_tcp - Attempt to do Van Jacobson header compression on a
* packet. This assumes that nb and comp are not null and that the first
* buffer of the chain contains a valid IP header.
* Return the VJ type code indicating whether or not the packet was
* compressed.
*/
u8_t
vj_compress_tcp(struct vjcompress *comp, struct pbuf **pb)
{
struct pbuf *np = *pb;
struct ip_hdr *ip = (struct ip_hdr *)np->payload;
struct cstate *cs = comp->last_cs->cs_next;
u16_t ilen = IPH_HL(ip);
u16_t hlen;
struct tcp_hdr *oth;
struct tcp_hdr *th;
u16_t deltaS, deltaA = 0;
u32_t deltaL;
u32_t changes = 0;
u8_t new_seq[16];
u8_t *cp = new_seq;
/*
* Check that the packet is IP proto TCP.
*/
if (IPH_PROTO(ip) != IP_PROTO_TCP) {
return (TYPE_IP);
}
/*
* Bail if this is an IP fragment or if the TCP packet isn't
* `compressible' (i.e., ACK isn't set or some other control bit is
* set).
*/
if ((IPH_OFFSET(ip) & PP_HTONS(0x3fff)) || np->tot_len < 40) {
return (TYPE_IP);
}
th = (struct tcp_hdr *)&((struct vj_u32_t*)ip)[ilen];
if ((TCPH_FLAGS(th) & (TCP_SYN|TCP_FIN|TCP_RST|TCP_ACK)) != TCP_ACK) {
return (TYPE_IP);
}
/* Check that the TCP/IP headers are contained in the first buffer. */
hlen = ilen + TCPH_HDRLEN(th);
hlen <<= 2;
if (np->len < hlen) {
PPPDEBUG(LOG_INFO, ("vj_compress_tcp: header len %d spans buffers\n", hlen));
return (TYPE_IP);
}
/* TCP stack requires that we don't change the packet payload, therefore we copy
* the whole packet before compression. */
np = pbuf_clone(PBUF_RAW, PBUF_RAM, *pb);
if (!np) {
return (TYPE_IP);
}
*pb = np;
ip = (struct ip_hdr *)np->payload;
/*
* Packet is compressible -- we're going to send either a
* COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need
* to locate (or create) the connection state. Special case the
* most recently used connection since it's most likely to be used
* again & we don't have to do any reordering if it's used.
*/
INCR(vjs_packets);
if (!ip4_addr_cmp(&ip->src, &cs->cs_ip.src)
|| !ip4_addr_cmp(&ip->dest, &cs->cs_ip.dest)
|| (*(struct vj_u32_t*)th).v != (((struct vj_u32_t*)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]).v) {
/*
* Wasn't the first -- search for it.
*
* States are kept in a circularly linked list with
* last_cs pointing to the end of the list. The
* list is kept in lru order by moving a state to the
* head of the list whenever it is referenced. Since
* the list is short and, empirically, the connection
* we want is almost always near the front, we locate
* states via linear search. If we don't find a state
* for the datagram, the oldest state is (re-)used.
*/
struct cstate *lcs;
struct cstate *lastcs = comp->last_cs;
do {
lcs = cs; cs = cs->cs_next;
INCR(vjs_searches);
if (ip4_addr_cmp(&ip->src, &cs->cs_ip.src)
&& ip4_addr_cmp(&ip->dest, &cs->cs_ip.dest)
&& (*(struct vj_u32_t*)th).v == (((struct vj_u32_t*)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]).v) {
goto found;
}
} while (cs != lastcs);
/*
* Didn't find it -- re-use oldest cstate. Send an
* uncompressed packet that tells the other side what
* connection number we're using for this conversation.
* Note that since the state list is circular, the oldest
* state points to the newest and we only need to set
* last_cs to update the lru linkage.
*/
INCR(vjs_misses);
comp->last_cs = lcs;
goto uncompressed;
found:
/*
* Found it -- move to the front on the connection list.
*/
if (cs == lastcs) {
comp->last_cs = lcs;
} else {
lcs->cs_next = cs->cs_next;
cs->cs_next = lastcs->cs_next;
lastcs->cs_next = cs;
}
}
oth = (struct tcp_hdr *)&((struct vj_u32_t*)&cs->cs_ip)[ilen];
deltaS = ilen;
/*
* Make sure that only what we expect to change changed. The first
* line of the `if' checks the IP protocol version, header length &
* type of service. The 2nd line checks the "Don't fragment" bit.
* The 3rd line checks the time-to-live and protocol (the protocol
* check is unnecessary but costless). The 4th line checks the TCP
* header length. The 5th line checks IP options, if any. The 6th
* line checks TCP options, if any. If any of these things are
* different between the previous & current datagram, we send the
* current datagram `uncompressed'.
*/
if ((((struct vj_u16_t*)ip)[0]).v != (((struct vj_u16_t*)&cs->cs_ip)[0]).v
|| (((struct vj_u16_t*)ip)[3]).v != (((struct vj_u16_t*)&cs->cs_ip)[3]).v
|| (((struct vj_u16_t*)ip)[4]).v != (((struct vj_u16_t*)&cs->cs_ip)[4]).v
|| TCPH_HDRLEN(th) != TCPH_HDRLEN(oth)
|| (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2))
|| (TCPH_HDRLEN(th) > 5 && BCMP(th + 1, oth + 1, (TCPH_HDRLEN(th) - 5) << 2))) {
goto uncompressed;
}
/*
* Figure out which of the changing fields changed. The
* receiver expects changes in the order: urgent, window,
* ack, seq (the order minimizes the number of temporaries
* needed in this section of code).
*/
if (TCPH_FLAGS(th) & TCP_URG) {
deltaS = lwip_ntohs(th->urgp);
ENCODEZ(deltaS);
changes |= NEW_U;
} else if (th->urgp != oth->urgp) {
/* argh! URG not set but urp changed -- a sensible
* implementation should never do this but RFC793
* doesn't prohibit the change so we have to deal
* with it. */
goto uncompressed;
}
if ((deltaS = (u16_t)(lwip_ntohs(th->wnd) - lwip_ntohs(oth->wnd))) != 0) {
ENCODE(deltaS);
changes |= NEW_W;
}
if ((deltaL = lwip_ntohl(th->ackno) - lwip_ntohl(oth->ackno)) != 0) {
if (deltaL > 0xffff) {
goto uncompressed;
}
deltaA = (u16_t)deltaL;
ENCODE(deltaA);
changes |= NEW_A;
}
if ((deltaL = lwip_ntohl(th->seqno) - lwip_ntohl(oth->seqno)) != 0) {
if (deltaL > 0xffff) {
goto uncompressed;
}
deltaS = (u16_t)deltaL;
ENCODE(deltaS);
changes |= NEW_S;
}
switch(changes) {
case 0:
/*
* Nothing changed. If this packet contains data and the
* last one didn't, this is probably a data packet following
* an ack (normal on an interactive connection) and we send
* it compressed. Otherwise it's probably a retransmit,
* retransmitted ack or window probe. Send it uncompressed
* in case the other side missed the compressed version.
*/
if (IPH_LEN(ip) != IPH_LEN(&cs->cs_ip) &&
lwip_ntohs(IPH_LEN(&cs->cs_ip)) == hlen) {
break;
}
/* no break */
/* fall through */
case SPECIAL_I:
case SPECIAL_D:
/*
* actual changes match one of our special case encodings --
* send packet uncompressed.
*/
goto uncompressed;
case NEW_S|NEW_A:
if (deltaS == deltaA && deltaS == lwip_ntohs(IPH_LEN(&cs->cs_ip)) - hlen) {
/* special case for echoed terminal traffic */
changes = SPECIAL_I;
cp = new_seq;
}
break;
case NEW_S:
if (deltaS == lwip_ntohs(IPH_LEN(&cs->cs_ip)) - hlen) {
/* special case for data xfer */
changes = SPECIAL_D;
cp = new_seq;
}
break;
default:
break;
}
deltaS = (u16_t)(lwip_ntohs(IPH_ID(ip)) - lwip_ntohs(IPH_ID(&cs->cs_ip)));
if (deltaS != 1) {
ENCODEZ(deltaS);
changes |= NEW_I;
}
if (TCPH_FLAGS(th) & TCP_PSH) {
changes |= TCP_PUSH_BIT;
}
/*
* Grab the cksum before we overwrite it below. Then update our
* state with this packet's header.
*/
deltaA = lwip_ntohs(th->chksum);
MEMCPY(&cs->cs_ip, ip, hlen);
/*
* We want to use the original packet as our compressed packet.
* (cp - new_seq) is the number of bytes we need for compressed
* sequence numbers. In addition we need one byte for the change
* mask, one for the connection id and two for the tcp checksum.
* So, (cp - new_seq) + 4 bytes of header are needed. hlen is how
* many bytes of the original packet to toss so subtract the two to
* get the new packet size.
*/
deltaS = (u16_t)(cp - new_seq);
if (!comp->compressSlot || comp->last_xmit != cs->cs_id) {
comp->last_xmit = cs->cs_id;
hlen -= deltaS + 4;
if (pbuf_remove_header(np, hlen)){
/* Can we cope with this failing? Just assert for now */
LWIP_ASSERT("pbuf_remove_header failed\n", 0);
}
cp = (u8_t*)np->payload;
*cp++ = (u8_t)(changes | NEW_C);
*cp++ = cs->cs_id;
} else {
hlen -= deltaS + 3;
if (pbuf_remove_header(np, hlen)) {
/* Can we cope with this failing? Just assert for now */
LWIP_ASSERT("pbuf_remove_header failed\n", 0);
}
cp = (u8_t*)np->payload;
*cp++ = (u8_t)changes;
}
*cp++ = (u8_t)(deltaA >> 8);
*cp++ = (u8_t)deltaA;
MEMCPY(cp, new_seq, deltaS);
INCR(vjs_compressed);
return (TYPE_COMPRESSED_TCP);
/*
* Update connection state cs & send uncompressed packet (that is,
* a regular ip/tcp packet but with the 'conversation id' we hope
* to use on future compressed packets in the protocol field).
*/
uncompressed:
MEMCPY(&cs->cs_ip, ip, hlen);
IPH_PROTO_SET(ip, cs->cs_id);
comp->last_xmit = cs->cs_id;
return (TYPE_UNCOMPRESSED_TCP);
}
/*
* Called when we may have missed a packet.
*/
void
vj_uncompress_err(struct vjcompress *comp)
{
comp->flags |= VJF_TOSS;
INCR(vjs_errorin);
}
/*
* "Uncompress" a packet of type TYPE_UNCOMPRESSED_TCP.
* Return 0 on success, -1 on failure.
*/
int
vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp)
{
u32_t hlen;
struct cstate *cs;
struct ip_hdr *ip;
ip = (struct ip_hdr *)nb->payload;
hlen = IPH_HL(ip) << 2;
if (IPH_PROTO(ip) >= MAX_SLOTS
|| hlen + sizeof(struct tcp_hdr) > nb->len
|| (hlen += TCPH_HDRLEN_BYTES((struct tcp_hdr *)&((char *)ip)[hlen]))
> nb->len
|| hlen > MAX_HDR) {
PPPDEBUG(LOG_INFO, ("vj_uncompress_uncomp: bad cid=%d, hlen=%d buflen=%d\n",
IPH_PROTO(ip), hlen, nb->len));
vj_uncompress_err(comp);
return -1;
}
cs = &comp->rstate[comp->last_recv = IPH_PROTO(ip)];
comp->flags &=~ VJF_TOSS;
IPH_PROTO_SET(ip, IP_PROTO_TCP);
/* copy from/to bigger buffers checked above instead of cs->cs_ip and ip
just to help static code analysis to see this is correct ;-) */
MEMCPY(&cs->cs_hdr, nb->payload, hlen);
cs->cs_hlen = (u16_t)hlen;
INCR(vjs_uncompressedin);
return 0;
}
/*
* Uncompress a packet of type TYPE_COMPRESSED_TCP.
* The packet is composed of a buffer chain and the first buffer
* must contain an accurate chain length.
* The first buffer must include the entire compressed TCP/IP header.
* This procedure replaces the compressed header with the uncompressed
* header and returns the length of the VJ header.
*/
int
vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp)
{
u8_t *cp;
struct tcp_hdr *th;
struct cstate *cs;
struct vj_u16_t *bp;
struct pbuf *n0 = *nb;
u32_t tmp;
u32_t vjlen, hlen, changes;
INCR(vjs_compressedin);
cp = (u8_t*)n0->payload;
changes = *cp++;
if (changes & NEW_C) {
/*
* Make sure the state index is in range, then grab the state.
* If we have a good state index, clear the 'discard' flag.
*/
if (*cp >= MAX_SLOTS) {
PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: bad cid=%d\n", *cp));
goto bad;
}
comp->flags &=~ VJF_TOSS;
comp->last_recv = *cp++;
} else {
/*
* this packet has an implicit state index. If we've
* had a line error since the last time we got an
* explicit state index, we have to toss the packet.
*/
if (comp->flags & VJF_TOSS) {
PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: tossing\n"));
INCR(vjs_tossed);
return (-1);
}
}
cs = &comp->rstate[comp->last_recv];
hlen = IPH_HL(&cs->cs_ip) << 2;
th = (struct tcp_hdr *)&((u8_t*)&cs->cs_ip)[hlen];
th->chksum = lwip_htons((*cp << 8) | cp[1]);
cp += 2;
if (changes & TCP_PUSH_BIT) {
TCPH_SET_FLAG(th, TCP_PSH);
} else {
TCPH_UNSET_FLAG(th, TCP_PSH);
}
switch (changes & SPECIALS_MASK) {
case SPECIAL_I:
{
u32_t i = lwip_ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen;
/* some compilers can't nest inline assembler.. */
tmp = lwip_ntohl(th->ackno) + i;
th->ackno = lwip_htonl(tmp);
tmp = lwip_ntohl(th->seqno) + i;
th->seqno = lwip_htonl(tmp);
}
break;
case SPECIAL_D:
/* some compilers can't nest inline assembler.. */
tmp = lwip_ntohl(th->seqno) + lwip_ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen;
th->seqno = lwip_htonl(tmp);
break;
default:
if (changes & NEW_U) {
TCPH_SET_FLAG(th, TCP_URG);
DECODEU(th->urgp);
} else {
TCPH_UNSET_FLAG(th, TCP_URG);
}
if (changes & NEW_W) {
DECODES(th->wnd);
}
if (changes & NEW_A) {
DECODEL(th->ackno);
}
if (changes & NEW_S) {
DECODEL(th->seqno);
}
break;
}
if (changes & NEW_I) {
DECODES(cs->cs_ip._id);
} else {
IPH_ID_SET(&cs->cs_ip, lwip_ntohs(IPH_ID(&cs->cs_ip)) + 1);
IPH_ID_SET(&cs->cs_ip, lwip_htons(IPH_ID(&cs->cs_ip)));
}
/*
* At this point, cp points to the first byte of data in the
* packet. Fill in the IP total length and update the IP
* header checksum.
*/
vjlen = (u16_t)(cp - (u8_t*)n0->payload);
if (n0->len < vjlen) {
/*
* We must have dropped some characters (crc should detect
* this but the old slip framing won't)
*/
PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: head buffer %d too short %d\n",
n0->len, vjlen));
goto bad;
}
#if BYTE_ORDER == LITTLE_ENDIAN
tmp = n0->tot_len - vjlen + cs->cs_hlen;
IPH_LEN_SET(&cs->cs_ip, lwip_htons((u16_t)tmp));
#else
IPH_LEN_SET(&cs->cs_ip, lwip_htons(n0->tot_len - vjlen + cs->cs_hlen));
#endif
/* recompute the ip header checksum */
bp = (struct vj_u16_t*) &cs->cs_ip;
IPH_CHKSUM_SET(&cs->cs_ip, 0);
for (tmp = 0; hlen > 0; hlen -= 2) {
tmp += (*bp++).v;
}
tmp = (tmp & 0xffff) + (tmp >> 16);
tmp = (tmp & 0xffff) + (tmp >> 16);
IPH_CHKSUM_SET(&cs->cs_ip, (u16_t)(~tmp));
/* Remove the compressed header and prepend the uncompressed header. */
if (pbuf_remove_header(n0, vjlen)) {
/* Can we cope with this failing? Just assert for now */
LWIP_ASSERT("pbuf_remove_header failed\n", 0);
goto bad;
}
if(LWIP_MEM_ALIGN(n0->payload) != n0->payload) {
struct pbuf *np;
#if IP_FORWARD
/* If IP forwarding is enabled we are using a PBUF_LINK packet type so
* the packet is being allocated with enough header space to be
* forwarded (to Ethernet for example).
*/
np = pbuf_alloc(PBUF_LINK, n0->len + cs->cs_hlen, PBUF_POOL);
#else /* IP_FORWARD */
np = pbuf_alloc(PBUF_RAW, n0->len + cs->cs_hlen, PBUF_POOL);
#endif /* IP_FORWARD */
if(!np) {
PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: realign failed\n"));
goto bad;
}
if (pbuf_remove_header(np, cs->cs_hlen)) {
/* Can we cope with this failing? Just assert for now */
LWIP_ASSERT("pbuf_remove_header failed\n", 0);
goto bad;
}
pbuf_take(np, n0->payload, n0->len);
if(n0->next) {
pbuf_chain(np, n0->next);
pbuf_dechain(n0);
}
pbuf_free(n0);
n0 = np;
}
if (pbuf_add_header(n0, cs->cs_hlen)) {
struct pbuf *np;
LWIP_ASSERT("vj_uncompress_tcp: cs->cs_hlen <= PBUF_POOL_BUFSIZE", cs->cs_hlen <= PBUF_POOL_BUFSIZE);
np = pbuf_alloc(PBUF_RAW, cs->cs_hlen, PBUF_POOL);
if(!np) {
PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: prepend failed\n"));
goto bad;
}
pbuf_cat(np, n0);
n0 = np;
}
LWIP_ASSERT("n0->len >= cs->cs_hlen", n0->len >= cs->cs_hlen);
MEMCPY(n0->payload, &cs->cs_ip, cs->cs_hlen);
*nb = n0;
return vjlen;
bad:
vj_uncompress_err(comp);
return (-1);
}
#endif /* PPP_SUPPORT && VJ_SUPPORT */

View File

@@ -0,0 +1,558 @@
/**
* @file
* SLIP Interface
*
*/
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute 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 INSTITUTE 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 INSTITUTE 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.
*
* This file is built upon the file: src/arch/rtxc/netif/sioslip.c
*
* Author: Magnus Ivarsson <magnus.ivarsson(at)volvo.com>
* Simon Goldschmidt
*/
/**
* @defgroup slipif SLIP
* @ingroup netifs
*
* This is an arch independent SLIP netif. The specific serial hooks must be
* provided by another file. They are sio_open, sio_read/sio_tryread and sio_send
*
* Usage: This netif can be used in three ways:\n
* 1) For NO_SYS==0, an RX thread can be used which blocks on sio_read()
* until data is received.\n
* 2) In your main loop, call slipif_poll() to check for new RX bytes,
* completed packets are fed into netif->input().\n
* 3) Call slipif_received_byte[s]() from your serial RX ISR and
* slipif_process_rxqueue() from your main loop. ISR level decodes
* packets and puts completed packets on a queue which is fed into
* the stack from the main loop (needs SYS_LIGHTWEIGHT_PROT for
* pbuf_alloc to work on ISR level!).
*
*/
#include "netif/slipif.h"
#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/pbuf.h"
#include "lwip/stats.h"
#include "lwip/snmp.h"
#include "lwip/sys.h"
#include "lwip/sio.h"
#define SLIP_END 0xC0 /* 0300: start and end of every packet */
#define SLIP_ESC 0xDB /* 0333: escape start (one byte escaped data follows) */
#define SLIP_ESC_END 0xDC /* 0334: following escape: original byte is 0xC0 (END) */
#define SLIP_ESC_ESC 0xDD /* 0335: following escape: original byte is 0xDB (ESC) */
/** Maximum packet size that is received by this netif */
#ifndef SLIP_MAX_SIZE
#define SLIP_MAX_SIZE 1500
#endif
/** Define this to the interface speed for SNMP
* (sio_fd is the sio_fd_t returned by sio_open).
* The default value of zero means 'unknown'.
*/
#ifndef SLIP_SIO_SPEED
#define SLIP_SIO_SPEED(sio_fd) 0
#endif
enum slipif_recv_state {
SLIP_RECV_NORMAL,
SLIP_RECV_ESCAPE
};
struct slipif_priv {
sio_fd_t sd;
/* q is the whole pbuf chain for a packet, p is the current pbuf in the chain */
struct pbuf *p, *q;
u8_t state;
u16_t i, recved;
#if SLIP_RX_FROM_ISR
struct pbuf *rxpackets;
#endif
};
/**
* Send a pbuf doing the necessary SLIP encapsulation
*
* Uses the serial layer's sio_send()
*
* @param netif the lwip network interface structure for this slipif
* @param p the pbuf chain packet to send
* @return always returns ERR_OK since the serial layer does not provide return values
*/
static err_t
slipif_output(struct netif *netif, struct pbuf *p)
{
struct slipif_priv *priv;
struct pbuf *q;
u16_t i;
u8_t c;
LWIP_ASSERT("netif != NULL", (netif != NULL));
LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
LWIP_ASSERT("p != NULL", (p != NULL));
LWIP_DEBUGF(SLIP_DEBUG, ("slipif_output: sending %"U16_F" bytes\n", p->tot_len));
priv = (struct slipif_priv *)netif->state;
/* Send pbuf out on the serial I/O device. */
/* Start with packet delimiter. */
sio_send(SLIP_END, priv->sd);
for (q = p; q != NULL; q = q->next) {
for (i = 0; i < q->len; i++) {
c = ((u8_t *)q->payload)[i];
switch (c) {
case SLIP_END:
/* need to escape this byte (0xC0 -> 0xDB, 0xDC) */
sio_send(SLIP_ESC, priv->sd);
sio_send(SLIP_ESC_END, priv->sd);
break;
case SLIP_ESC:
/* need to escape this byte (0xDB -> 0xDB, 0xDD) */
sio_send(SLIP_ESC, priv->sd);
sio_send(SLIP_ESC_ESC, priv->sd);
break;
default:
/* normal byte - no need for escaping */
sio_send(c, priv->sd);
break;
}
}
}
/* End with packet delimiter. */
sio_send(SLIP_END, priv->sd);
return ERR_OK;
}
#if LWIP_IPV4
/**
* Send a pbuf doing the necessary SLIP encapsulation
*
* Uses the serial layer's sio_send()
*
* @param netif the lwip network interface structure for this slipif
* @param p the pbuf chain packet to send
* @param ipaddr the ip address to send the packet to (not used for slipif)
* @return always returns ERR_OK since the serial layer does not provide return values
*/
static err_t
slipif_output_v4(struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr)
{
LWIP_UNUSED_ARG(ipaddr);
return slipif_output(netif, p);
}
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
/**
* Send a pbuf doing the necessary SLIP encapsulation
*
* Uses the serial layer's sio_send()
*
* @param netif the lwip network interface structure for this slipif
* @param p the pbuf chain packet to send
* @param ipaddr the ip address to send the packet to (not used for slipif)
* @return always returns ERR_OK since the serial layer does not provide return values
*/
static err_t
slipif_output_v6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr)
{
LWIP_UNUSED_ARG(ipaddr);
return slipif_output(netif, p);
}
#endif /* LWIP_IPV6 */
/**
* Handle the incoming SLIP stream character by character
*
* @param netif the lwip network interface structure for this slipif
* @param c received character (multiple calls to this function will
* return a complete packet, NULL is returned before - used for polling)
* @return The IP packet when SLIP_END is received
*/
static struct pbuf *
slipif_rxbyte(struct netif *netif, u8_t c)
{
struct slipif_priv *priv;
struct pbuf *t;
LWIP_ASSERT("netif != NULL", (netif != NULL));
LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
priv = (struct slipif_priv *)netif->state;
switch (priv->state) {
case SLIP_RECV_NORMAL:
switch (c) {
case SLIP_END:
if (priv->recved > 0) {
/* Received whole packet. */
/* Trim the pbuf to the size of the received packet. */
pbuf_realloc(priv->q, priv->recved);
LINK_STATS_INC(link.recv);
LWIP_DEBUGF(SLIP_DEBUG, ("slipif: Got packet (%"U16_F" bytes)\n", priv->recved));
t = priv->q;
priv->p = priv->q = NULL;
priv->i = priv->recved = 0;
return t;
}
return NULL;
case SLIP_ESC:
priv->state = SLIP_RECV_ESCAPE;
return NULL;
default:
break;
} /* end switch (c) */
break;
case SLIP_RECV_ESCAPE:
/* un-escape END or ESC bytes, leave other bytes
(although that would be a protocol error) */
switch (c) {
case SLIP_ESC_END:
c = SLIP_END;
break;
case SLIP_ESC_ESC:
c = SLIP_ESC;
break;
default:
break;
}
priv->state = SLIP_RECV_NORMAL;
break;
default:
break;
} /* end switch (priv->state) */
/* byte received, packet not yet completely received */
if (priv->p == NULL) {
/* allocate a new pbuf */
LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: alloc\n"));
priv->p = pbuf_alloc(PBUF_LINK, (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN - PBUF_LINK_ENCAPSULATION_HLEN), PBUF_POOL);
if (priv->p == NULL) {
LINK_STATS_INC(link.drop);
LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: no new pbuf! (DROP)\n"));
/* don't process any further since we got no pbuf to receive to */
return NULL;
}
if (priv->q != NULL) {
/* 'chain' the pbuf to the existing chain */
pbuf_cat(priv->q, priv->p);
} else {
/* p is the first pbuf in the chain */
priv->q = priv->p;
}
}
/* this automatically drops bytes if > SLIP_MAX_SIZE */
if ((priv->p != NULL) && (priv->recved <= SLIP_MAX_SIZE)) {
((u8_t *)priv->p->payload)[priv->i] = c;
priv->recved++;
priv->i++;
if (priv->i >= priv->p->len) {
/* on to the next pbuf */
priv->i = 0;
if (priv->p->next != NULL && priv->p->next->len > 0) {
/* p is a chain, on to the next in the chain */
priv->p = priv->p->next;
} else {
/* p is a single pbuf, set it to NULL so next time a new
* pbuf is allocated */
priv->p = NULL;
}
}
}
return NULL;
}
/** Like slipif_rxbyte, but passes completed packets to netif->input
*
* @param netif The lwip network interface structure for this slipif
* @param c received character
*/
static void
slipif_rxbyte_input(struct netif *netif, u8_t c)
{
struct pbuf *p;
p = slipif_rxbyte(netif, c);
if (p != NULL) {
if (netif->input(p, netif) != ERR_OK) {
pbuf_free(p);
}
}
}
#if SLIP_USE_RX_THREAD
/**
* The SLIP input thread.
*
* Feed the IP layer with incoming packets
*
* @param nf the lwip network interface structure for this slipif
*/
static void
slipif_loop_thread(void *nf)
{
u8_t c;
struct netif *netif = (struct netif *)nf;
struct slipif_priv *priv = (struct slipif_priv *)netif->state;
while (1) {
if (sio_read(priv->sd, &c, 1) > 0) {
slipif_rxbyte_input(netif, c);
}
}
}
#endif /* SLIP_USE_RX_THREAD */
/**
* @ingroup slipif
* SLIP netif initialization
*
* Call the arch specific sio_open and remember
* the opened device in the state field of the netif.
*
* @param netif the lwip network interface structure for this slipif
* @return ERR_OK if serial line could be opened,
* ERR_MEM if no memory could be allocated,
* ERR_IF is serial line couldn't be opened
*
* @note If netif->state is interpreted as an u8_t serial port number.
*
*/
err_t
slipif_init(struct netif *netif)
{
struct slipif_priv *priv;
u8_t sio_num;
LWIP_ASSERT("slipif needs an input callback", netif->input != NULL);
/* netif->state contains serial port number */
sio_num = LWIP_PTR_NUMERIC_CAST(u8_t, netif->state);
LWIP_DEBUGF(SLIP_DEBUG, ("slipif_init: netif->num=%"U16_F"\n", (u16_t)sio_num));
/* Allocate private data */
priv = (struct slipif_priv *)mem_malloc(sizeof(struct slipif_priv));
if (!priv) {
return ERR_MEM;
}
netif->name[0] = 's';
netif->name[1] = 'l';
#if LWIP_IPV4
netif->output = slipif_output_v4;
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
netif->output_ip6 = slipif_output_v6;
#endif /* LWIP_IPV6 */
netif->mtu = SLIP_MAX_SIZE;
/* Try to open the serial port. */
priv->sd = sio_open(sio_num);
if (!priv->sd) {
/* Opening the serial port failed. */
mem_free(priv);
return ERR_IF;
}
/* Initialize private data */
priv->p = NULL;
priv->q = NULL;
priv->state = SLIP_RECV_NORMAL;
priv->i = 0;
priv->recved = 0;
#if SLIP_RX_FROM_ISR
priv->rxpackets = NULL;
#endif
netif->state = priv;
/* initialize the snmp variables and counters inside the struct netif */
MIB2_INIT_NETIF(netif, snmp_ifType_slip, SLIP_SIO_SPEED(priv->sd));
#if SLIP_USE_RX_THREAD
/* Create a thread to poll the serial line. */
sys_thread_new(SLIPIF_THREAD_NAME, slipif_loop_thread, netif,
SLIPIF_THREAD_STACKSIZE, SLIPIF_THREAD_PRIO);
#endif /* SLIP_USE_RX_THREAD */
return ERR_OK;
}
/**
* @ingroup slipif
* Polls the serial device and feeds the IP layer with incoming packets.
*
* @param netif The lwip network interface structure for this slipif
*/
void
slipif_poll(struct netif *netif)
{
u8_t c;
struct slipif_priv *priv;
LWIP_ASSERT("netif != NULL", (netif != NULL));
LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
priv = (struct slipif_priv *)netif->state;
while (sio_tryread(priv->sd, &c, 1) > 0) {
slipif_rxbyte_input(netif, c);
}
}
#if SLIP_RX_FROM_ISR
/**
* @ingroup slipif
* Feeds the IP layer with incoming packets that were receive
*
* @param netif The lwip network interface structure for this slipif
*/
void
slipif_process_rxqueue(struct netif *netif)
{
struct slipif_priv *priv;
SYS_ARCH_DECL_PROTECT(old_level);
LWIP_ASSERT("netif != NULL", (netif != NULL));
LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
priv = (struct slipif_priv *)netif->state;
SYS_ARCH_PROTECT(old_level);
while (priv->rxpackets != NULL) {
struct pbuf *p = priv->rxpackets;
#if SLIP_RX_QUEUE
/* dequeue packet */
struct pbuf *q = p;
while ((q->len != q->tot_len) && (q->next != NULL)) {
q = q->next;
}
priv->rxpackets = q->next;
q->next = NULL;
#else /* SLIP_RX_QUEUE */
priv->rxpackets = NULL;
#endif /* SLIP_RX_QUEUE */
SYS_ARCH_UNPROTECT(old_level);
if (netif->input(p, netif) != ERR_OK) {
pbuf_free(p);
}
SYS_ARCH_PROTECT(old_level);
}
SYS_ARCH_UNPROTECT(old_level);
}
/** Like slipif_rxbyte, but queues completed packets.
*
* @param netif The lwip network interface structure for this slipif
* @param data Received serial byte
*/
static void
slipif_rxbyte_enqueue(struct netif *netif, u8_t data)
{
struct pbuf *p;
struct slipif_priv *priv = (struct slipif_priv *)netif->state;
SYS_ARCH_DECL_PROTECT(old_level);
p = slipif_rxbyte(netif, data);
if (p != NULL) {
SYS_ARCH_PROTECT(old_level);
if (priv->rxpackets != NULL) {
#if SLIP_RX_QUEUE
/* queue multiple pbufs */
struct pbuf *q = p;
while (q->next != NULL) {
q = q->next;
}
q->next = p;
} else {
#else /* SLIP_RX_QUEUE */
pbuf_free(priv->rxpackets);
}
{
#endif /* SLIP_RX_QUEUE */
priv->rxpackets = p;
}
SYS_ARCH_UNPROTECT(old_level);
}
}
/**
* @ingroup slipif
* Process a received byte, completed packets are put on a queue that is
* fed into IP through slipif_process_rxqueue().
*
* This function can be called from ISR if SYS_LIGHTWEIGHT_PROT is enabled.
*
* @param netif The lwip network interface structure for this slipif
* @param data received character
*/
void
slipif_received_byte(struct netif *netif, u8_t data)
{
LWIP_ASSERT("netif != NULL", (netif != NULL));
LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
slipif_rxbyte_enqueue(netif, data);
}
/**
* @ingroup slipif
* Process multiple received byte, completed packets are put on a queue that is
* fed into IP through slipif_process_rxqueue().
*
* This function can be called from ISR if SYS_LIGHTWEIGHT_PROT is enabled.
*
* @param netif The lwip network interface structure for this slipif
* @param data received character
* @param len Number of received characters
*/
void
slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len)
{
u8_t i;
u8_t *rxdata = data;
LWIP_ASSERT("netif != NULL", (netif != NULL));
LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
for (i = 0; i < len; i++, rxdata++) {
slipif_rxbyte_enqueue(netif, *rxdata);
}
}
#endif /* SLIP_RX_FROM_ISR */

View File

@@ -0,0 +1,300 @@
/**
* @file
*
* @defgroup zepif ZEP - ZigBee Encapsulation Protocol
* @ingroup netifs
* A netif implementing the ZigBee Encapsulation Protocol (ZEP).
* This is used to tunnel 6LowPAN over UDP.
*
* Usage (there must be a default netif before!):
* @code{.c}
* netif_add(&zep_netif, NULL, NULL, NULL, NULL, zepif_init, tcpip_6lowpan_input);
* netif_create_ip6_linklocal_address(&zep_netif, 1);
* netif_set_up(&zep_netif);
* netif_set_link_up(&zep_netif);
* @endcode
*/
/*
* Copyright (c) 2018 Simon Goldschmidt
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 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.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: Simon Goldschmidt <goldsimon@gmx.de>
*
*/
#include "netif/zepif.h"
#if LWIP_IPV6 && LWIP_UDP
#include "netif/lowpan6.h"
#include "lwip/udp.h"
#include "lwip/timeouts.h"
#include <string.h>
/** Define this to 1 to loop back TX packets for testing */
#ifndef ZEPIF_LOOPBACK
#define ZEPIF_LOOPBACK 0
#endif
#define ZEP_MAX_DATA_LEN 127
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/bpstruct.h"
#endif
PACK_STRUCT_BEGIN
struct zep_hdr {
PACK_STRUCT_FLD_8(u8_t prot_id[2]);
PACK_STRUCT_FLD_8(u8_t prot_version);
PACK_STRUCT_FLD_8(u8_t type);
PACK_STRUCT_FLD_8(u8_t channel_id);
PACK_STRUCT_FIELD(u16_t device_id);
PACK_STRUCT_FLD_8(u8_t crc_mode);
PACK_STRUCT_FLD_8(u8_t unknown_1);
PACK_STRUCT_FIELD(u32_t timestamp[2]);
PACK_STRUCT_FIELD(u32_t seq_num);
PACK_STRUCT_FLD_8(u8_t unknown_2[10]);
PACK_STRUCT_FLD_8(u8_t len);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/epstruct.h"
#endif
struct zepif_state {
struct zepif_init init;
struct udp_pcb *pcb;
u32_t seqno;
};
static u8_t zep_lowpan_timer_running;
/* Helper function that calls the 6LoWPAN timer and reschedules itself */
static void
zep_lowpan_timer(void *arg)
{
lowpan6_tmr();
if (zep_lowpan_timer_running) {
sys_timeout(LOWPAN6_TMR_INTERVAL, zep_lowpan_timer, arg);
}
}
/* Pass received pbufs into 6LowPAN netif */
static void
zepif_udp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p,
const ip_addr_t *addr, u16_t port)
{
err_t err;
struct netif *netif_lowpan6 = (struct netif *)arg;
struct zep_hdr *zep;
LWIP_ASSERT("arg != NULL", arg != NULL);
LWIP_ASSERT("pcb != NULL", pcb != NULL);
LWIP_UNUSED_ARG(pcb); /* for LWIP_NOASSERT */
LWIP_UNUSED_ARG(addr);
LWIP_UNUSED_ARG(port);
if (p == NULL) {
return;
}
/* Parse and hide the ZEP header */
if (p->len < sizeof(struct zep_hdr)) {
/* need the zep_hdr in one piece */
goto err_return;
}
zep = (struct zep_hdr *)p->payload;
if (zep->prot_id[0] != 'E') {
goto err_return;
}
if (zep->prot_id[1] != 'X') {
goto err_return;
}
if (zep->prot_version != 2) {
/* we only support this version for now */
goto err_return;
}
if (zep->type != 1) {
goto err_return;
}
if (zep->crc_mode != 1) {
goto err_return;
}
if (zep->len != p->tot_len - sizeof(struct zep_hdr)) {
goto err_return;
}
/* everything seems to be OK, hide the ZEP header */
if (pbuf_remove_header(p, sizeof(struct zep_hdr))) {
goto err_return;
}
/* TODO Check CRC? */
/* remove CRC trailer */
pbuf_realloc(p, p->tot_len - 2);
/* Call into 6LoWPAN code. */
err = netif_lowpan6->input(p, netif_lowpan6);
if (err == ERR_OK) {
return;
}
err_return:
pbuf_free(p);
}
/* Send 6LoWPAN TX packets as UDP broadcast */
static err_t
zepif_linkoutput(struct netif *netif, struct pbuf *p)
{
err_t err;
struct pbuf *q;
struct zep_hdr *zep;
struct zepif_state *state;
LWIP_ASSERT("invalid netif", netif != NULL);
LWIP_ASSERT("invalid pbuf", p != NULL);
if (p->tot_len > ZEP_MAX_DATA_LEN) {
return ERR_VAL;
}
LWIP_ASSERT("TODO: support chained pbufs", p->next == NULL);
state = (struct zepif_state *)netif->state;
LWIP_ASSERT("state->pcb != NULL", state->pcb != NULL);
q = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct zep_hdr) + p->tot_len, PBUF_RAM);
if (q == NULL) {
return ERR_MEM;
}
zep = (struct zep_hdr *)q->payload;
memset(zep, 0, sizeof(struct zep_hdr));
zep->prot_id[0] = 'E';
zep->prot_id[1] = 'X';
zep->prot_version = 2;
zep->type = 1; /* Data */
zep->channel_id = 0; /* whatever */
zep->device_id = lwip_htons(1); /* whatever */
zep->crc_mode = 1;
zep->unknown_1 = 0xff;
zep->seq_num = lwip_htonl(state->seqno);
state->seqno++;
zep->len = (u8_t)p->tot_len;
err = pbuf_take_at(q, p->payload, p->tot_len, sizeof(struct zep_hdr));
if (err == ERR_OK) {
#if ZEPIF_LOOPBACK
zepif_udp_recv(netif, state->pcb, pbuf_clone(PBUF_RAW, PBUF_RAM, q), NULL, 0);
#endif
err = udp_sendto(state->pcb, q, state->init.zep_dst_ip_addr, state->init.zep_dst_udp_port);
}
pbuf_free(q);
return err;
}
/**
* @ingroup zepif
* Set up a raw 6LowPAN netif and surround it with input- and output
* functions for ZEP
*/
err_t
zepif_init(struct netif *netif)
{
err_t err;
struct zepif_init *init_state = (struct zepif_init *)netif->state;
struct zepif_state *state = (struct zepif_state *)mem_malloc(sizeof(struct zepif_state));
LWIP_ASSERT("zepif needs an input callback", netif->input != NULL);
if (state == NULL) {
return ERR_MEM;
}
memset(state, 0, sizeof(struct zepif_state));
if (init_state != NULL) {
memcpy(&state->init, init_state, sizeof(struct zepif_init));
}
if (state->init.zep_src_udp_port == 0) {
state->init.zep_src_udp_port = ZEPIF_DEFAULT_UDP_PORT;
}
if (state->init.zep_dst_udp_port == 0) {
state->init.zep_dst_udp_port = ZEPIF_DEFAULT_UDP_PORT;
}
#if LWIP_IPV4
if (state->init.zep_dst_ip_addr == NULL) {
/* With IPv4 enabled, default to broadcasting packets if no address is set */
state->init.zep_dst_ip_addr = IP_ADDR_BROADCAST;
}
#endif /* LWIP_IPV4 */
netif->state = NULL;
state->pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
if (state->pcb == NULL) {
err = ERR_MEM;
goto err_ret;
}
err = udp_bind(state->pcb, state->init.zep_src_ip_addr, state->init.zep_src_udp_port);
if (err != ERR_OK) {
goto err_ret;
}
if (state->init.zep_netif != NULL) {
udp_bind_netif(state->pcb, state->init.zep_netif);
}
LWIP_ASSERT("udp_bind(lowpan6_broadcast_pcb) failed", err == ERR_OK);
ip_set_option(state->pcb, SOF_BROADCAST);
udp_recv(state->pcb, zepif_udp_recv, netif);
err = lowpan6_if_init(netif);
LWIP_ASSERT("lowpan6_if_init set a state", netif->state == NULL);
if (err == ERR_OK) {
netif->state = state;
netif->hwaddr_len = 6;
if (init_state != NULL) {
memcpy(netif->hwaddr, init_state->addr, 6);
} else {
u8_t i;
for (i = 0; i < 6; i++) {
netif->hwaddr[i] = i;
}
netif->hwaddr[0] &= 0xfc;
}
netif->linkoutput = zepif_linkoutput;
if (!zep_lowpan_timer_running) {
sys_timeout(LOWPAN6_TMR_INTERVAL, zep_lowpan_timer, NULL);
zep_lowpan_timer_running = 1;
}
return ERR_OK;
}
err_ret:
if (state->pcb != NULL) {
udp_remove(state->pcb);
}
mem_free(state);
return err;
}
#endif /* LWIP_IPV6 && LWIP_UDP */