blob: d3d7d4ac19d3987957b7d4853c8fac88b119f208 [file] [log] [blame]
/*
* Copyright 2016 Google Inc.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <assert.h>
#include <stdio.h>
#include "base/container_of.h"
#include "base/die.h"
#include "base/init_funcs.h"
#include "base/list.h"
#include "base/xalloc.h"
#include "net/ethernet.h"
#include "net/ipv6/inet6.h"
#include "net/ipv6/ipv6.h"
#include "net/net.h"
ListNode ipv6_udp_connections;
// Useful addresses.
const Ipv6Address Ipv6LlAllNodes = {
.x = {0xFF, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
};
static int ipv6_udp_con_init(Ipv6UdpCon *con)
{
NetDevice *dev = con->dev;
ipv6_init(dev);
list_insert_after(&con->list_node, &ipv6_udp_connections);
con->initialized = 1;
return 0;
}
static int ipv6_udp_bind(NetConOps *me, uint16_t port)
{
Ipv6UdpCon *con = container_of(me, Ipv6UdpCon, ops);
if (!con->initialized && ipv6_udp_con_init(con))
return 1;
con->source_port = port;
return 0;
}
static int ipv6_udp_incoming(NetConOps *me, size_t *size)
{
Ipv6UdpCon *con = container_of(me, Ipv6UdpCon, ops);
if (!con->initialized && ipv6_udp_con_init(con))
return 1;
*size = con->incoming_count;
// Poke the device to receive any data it has.
if (!*size) {
net_service_dev(con->dev);
*size = con->incoming_count;
}
return 0;
}
static int ipv6_udp_send(NetConOps *me, const void *data, size_t len)
{
Ipv6UdpCon *con = container_of(me, Ipv6UdpCon, ops);
if (!con->initialized && ipv6_udp_con_init(con))
return 1;
return udpv6_send(con, data, len);
}
static int ipv6_udp_receive(NetConOps *me, void *data, size_t *len,
size_t max_len)
{
Ipv6UdpCon *con = container_of(me, Ipv6UdpCon, ops);
if (!con->initialized && ipv6_udp_con_init(con))
return 1;
size_t size;
if (me->incoming(me, &size))
return 1;
if (!size) {
printf("No data to receive.\n");
return 1;
} else if (size > max_len) {
printf("Insufficient space: putting %zd bytes in %zd bytes.\n",
size, max_len);
return 1;
}
*len = size;
con->incoming_count = 0;
if (data)
memcpy(data, con->incoming, size);
return 0;
}
static int ipv6_udp_close(NetConOps *me)
{
Ipv6UdpCon *con = container_of(me, Ipv6UdpCon, ops);
if (!con->initialized && ipv6_udp_con_init(con))
return 1;
list_remove(&con->list_node);
con->initialized = 0;
return 0;
}
Ipv6UdpCon *new_ipv6_udp_con(NetDevice *dev, const Ipv6Address *remote_ip,
uint16_t port)
{
Ipv6UdpCon *con = xzalloc(sizeof(*con));
con->ops.bind = &ipv6_udp_bind;
con->ops.incoming = &ipv6_udp_incoming;
con->ops.send = &ipv6_udp_send;
con->ops.receive = &ipv6_udp_receive;
con->ops.close = &ipv6_udp_close;
memcpy(&con->dest_ip, remote_ip, sizeof(con->dest_ip));
con->dest_port = port;
con->dev = dev;
return con;
}
int eth_add_mcast_filter(NetDevice *dev, const MacAddress *addr)
{
return 0;
}
void udpv6_recv(void *data, size_t len,
const Ipv6Address *daddr, uint16_t dport,
const Ipv6Address *saddr, uint16_t sport)
{
// Figure out who's data this is.
Ipv6UdpCon *con;
list_for_each(con, ipv6_udp_connections, list_node) {
if (con->source_port == dport) {
con->dest_port = sport;
memcpy(&con->dest_ip, saddr, sizeof(con->dest_ip));
if (con->incoming_count) {
printf("Already have data. Dropping.\n");
return;
}
if (len > sizeof(con->incoming)) {
printf("Too much data. Dropping.\n");
return;
}
con->incoming_count = len;
memcpy(con->incoming, data, con->incoming_count);
return;
}
}
}
static int ipv6_process(EthTypeOps *me, NetDevice *dev, void *data, size_t len)
{
return eth_recv(dev, data, len);
}
static EthTypeOps ipv6_type = {
.type = EthType_Ipv6,
.process = &ipv6_process,
};
static int ipv6_install_eth_types(void)
{
list_insert_after(&ipv6_type.list_node, &net_eth_types);
return 0;
}
INIT_FUNC(ipv6_install_eth_types);