diff --git a/Makefile.am b/Makefile.am index 7f323cb92..aff07ecf7 100644 --- a/Makefile.am +++ b/Makefile.am @@ -37,7 +37,9 @@ libpacketgraph_la_SOURCES = \ src/switch.c\ src/pmtud.c\ src/thread.c\ - src/ip-fragment.c + src/ip-fragment.c\ + src/iprange.c\ + src/dhcp.c pkginclude_HEADERS = \ include/packetgraph/common.h\ @@ -63,7 +65,8 @@ pkginclude_HEADERS = \ include/packetgraph/queue.h\ include/packetgraph/pmtud.h\ include/packetgraph/ip-fragment.h\ - include/packetgraph/errors.h + include/packetgraph/errors.h\ + include/packetgraph/dhcp.h libpacketgraph_la_LIBADD = $(RTE_SDK_LIBS) $(GLIB_LIBS) # FIXME '^pg_[^_]' does not take all symbols needed (i.e. __pg_error_*) @@ -76,7 +79,7 @@ include npfmakefile.am dist_doc_DATA = README.md -check_PROGRAMS = tests-antispoof tests-core tests-diode tests-rxtx tests-firewall tests-integration tests-nic tests-print tests-queue tests-switch tests-vhost tests-vtep tests-pmtud tests-tap tests-ip-fragment tests-thread +check_PROGRAMS = tests-antispoof tests-core tests-diode tests-rxtx tests-firewall tests-integration tests-nic tests-print tests-queue tests-switch tests-vhost tests-vtep tests-pmtud tests-tap tests-ip-fragment tests-thread tests-dhcp noinst_LTLIBRARIES += libpacketgraph-dev.la libpacketgraph_dev_la_SOURCES = $(libpacketgraph_la_SOURCES) @@ -195,6 +198,12 @@ tests_thread_CFLAGS = $(libpacketgraph_dev_la_CFLAGS) tests_thread_LDFLAGS = libpacketgraph-dev.la EXTRA_tests_thread_DEPENDENCIES = libpacketgraph-dev.la +tests_dhcp_SOURCES = \ + tests/dhcp/tests.c +tests_dhcp_CFLAGS = $(libpacketgraph_dev_la_CFLAGS) +tests_dhcp_LDFLAGS = libpacketgraph-dev.la +EXTRA_tests_thread_DEPENDENCIES = libpacketgraph-dev.la + TESTS = \ tests/antispoof/test.sh\ tests/core/test.sh\ @@ -211,7 +220,8 @@ TESTS = \ tests/tap/test.sh\ tests/thread/test.sh\ tests/integration/test.sh\ - tests/vhost/test.sh + tests/vhost/test.sh\ + tests/dhcp/test.sh noinst_PROGRAMS = @@ -408,4 +418,4 @@ doc: doxygen.conf $(srcdir)/doc/deploy_documentation.sh # libtoolize recommanded this -ACLOCAL_AMFLAGS = -I m4 +ACLOCAL_AMFLAGS = -I diff --git a/configure_clang b/configure_clang index bd8438867..bd35e987e 100755 --- a/configure_clang +++ b/configure_clang @@ -1,7 +1,4 @@ #!/bin/bash export CC="clang" -export AR="llvm-ar" -export NM="llvm-nm" -export RANLIB="llvm-ranlib" root=$(cd "$(dirname $0)" && pwd) -$root/configure ${@:1} +$root/configure CFLAGS="-O0 -g" ${@:1} diff --git a/include/packetgraph/dhcp.h b/include/packetgraph/dhcp.h new file mode 100644 index 000000000..4b508bb81 --- /dev/null +++ b/include/packetgraph/dhcp.h @@ -0,0 +1,77 @@ +/* Copyright 2017 Outscale SAS + * + * This file is part of Butterfly. + * + * Butterfly is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * Butterfly 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 Butterfly. If not, see . + */ + +#ifndef _PG_DHCP_H +#define _PG_DHCP_H + +#include +#include +#include + +/** + * Create a new dhcp brick. + * dhcp brick allow ip adress to mac adresses + * @name: name of the brick + * @cidr: range of ip adresses + * @errp: set in case of an error + * @return: a pointer to a brick structure on success, NULL on error + **/ + +struct pg_brick *pg_dhcp_new(const char *name, const char *cidr, + struct ether_addr mac_dhcp, + struct pg_error **errp); + +/** + * Calculate the number of potential adresses + * @ipcidr : ip adress with cidr to calculate the range + */ +int pg_calcul_range_ip(network_addr_t ipcidr); + +/** + * Check if a packet is a dhcp discover packet + * @pkts : the packet to check + */ +bool is_discover(struct rte_mbuf *pkts); + +/** + * Check if a packet is a dhcp discover packet + * @pkts : the packet to check + */ +bool is_request(struct rte_mbuf *pkts); + +/** + * Create dhcp packet to burst + * @msg_type : dhcp message type : 1 : Discover + * 2 : Offer + * 3 : Request + * 4 : Decline + * 5 : Acknowledgment + * 6 : Negative Acknowledgment + * 7 : Realease + * 8 : Informationnal + * @mac : Mac adresse of the brick + * @ip_offer : ip to give to the brick + * @bail : number of seconds for bail of the ip adresse to the brick + * @ip_router : ip of the default gateaway + * @mask : subnet mask used by the client + */ +struct rte_mbuf **pg_dhcp_packet_create(struct pg_brick *brick, + uint8_t msg_type, struct ether_addr mac, + uint32_t ip_offer, int bail, + uint32_t ip_router, uint32_t mask); + +#endif /* _PG_DHCP_H */ diff --git a/include/packetgraph/iprange.h b/include/packetgraph/iprange.h new file mode 100644 index 000000000..26c5552d6 --- /dev/null +++ b/include/packetgraph/iprange.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2003 Gabriel L. Somlo + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * To compile: + * on Linux: + * gcc -o iprange iprange.c -O2 -Wall + * on Solaris 8, Studio 8 CC: + * cc -xO5 -xarch=v8plusa -xdepend iprange.c -o iprange -lnsl -lresolv + * + * CHANGELOG : + * 2004-10-16 Paul Townsend (alpha alpha beta at purdue dot edu) + * more general input/output formatting + */ + +#include +#include +#include +#include +#include +#include +#include + +/*---------------------------------------------------------------------*/ +/* network address type: one field for the net address, one for prefix */ +/*---------------------------------------------------------------------*/ +typedef struct network_addr { + in_addr_t addr; + int pfx; +} network_addr_t; + +/*------------------------------------------------------------------*/ +/* Set a bit to a given value (0 or 1); MSB is bit 1, LSB is bit 32 */ +/*------------------------------------------------------------------*/ +in_addr_t set_bit(in_addr_t addr, int bitno, int val); + +/*----------------------------------------------------*/ +/* Compute broadcast address given address and prefix */ +/*----------------------------------------------------*/ +in_addr_t broadcast(in_addr_t addr, int prefix); + +/*--------------------------------------*/ +/* Compute netmask address given prefix */ +/*--------------------------------------*/ +in_addr_t netmask(int prefix); + +/*------------------------------------------------*/ +/* Print out a 32-bit address in A.B.C.D/M format */ +/*------------------------------------------------*/ +void print_addr(in_addr_t addr, int prefix); + +/*-----------------------------------------------------------*/ +/* Convert an A.B.C.D address into a 32-bit host-order value */ +/*-----------------------------------------------------------*/ +in_addr_t a_to_hl(const char *ipstr); + +/*--------------------------------------------------*/ +/* Compute network address given address and prefix */ +/*--------------------------------------------------*/ +in_addr_t network(in_addr_t addr, int prefix); + +/*-----------------------------------------------------------------*/ +/* convert a network address char string into a host-order network */ +/* address and an integer prefix value */ +/*-----------------------------------------------------------------*/ +int str_to_netaddr(const char *ipstr, network_addr_t *netaddr); + +/*------------------------------------------------------*/ +/* Print out an address range in a.b.c.d-A.B.C.D format */ +/*------------------------------------------------------*/ +void print_addr_range(in_addr_t lo, in_addr_t hi); diff --git a/include/packetgraph/packetgraph.h b/include/packetgraph/packetgraph.h index cd1837192..772d30748 100644 --- a/include/packetgraph/packetgraph.h +++ b/include/packetgraph/packetgraph.h @@ -42,5 +42,7 @@ #include #include #include +#include +#include #endif /* _PG_PACKETGRAPH_H */ diff --git a/src/dhcp.c b/src/dhcp.c new file mode 100644 index 000000000..9a1f896f0 --- /dev/null +++ b/src/dhcp.c @@ -0,0 +1,342 @@ +/* Copyright 2017 Outscale SAS + * + * This file is part of Butterfly. + * + * Butterfly is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * Butterfly 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 Butterfly. If not, see . + */ + +#include +#include +#include +#include +#include +#include "utils/mac.h" + +#include +#include "utils/bitmask.h" +#include "utils/mempool.h" +#include "packets.h" +#include "brick-int.h" +#include "utils/network.h" +#include "src/npf/npf/dpdk/npf_dpdk.h" + +#define DHCP_SIDE_TO_NPF(side) \ + ((side) == PG_WEST_SIDE ? PFIL_OUT : PFIL_IN) + +bool eth_compare(uint8_t eth1[6], uint8_t eth2[6]); + +struct dhcp_messages_payload { + uint8_t op; + uint8_t htype; + uint8_t hlen; + uint8_t hops; + uint32_t xid; + uint16_t secs; + uint16_t flags; + uint32_t ciaddr; + uint32_t yiaddr; + uint32_t siaddr; + uint32_t giaddr; + struct ether_addr mac_client; + char * server_name; + char * file; + uint8_t dhcp_m_type; + struct ether_addr client_id; + uint32_t subnet_mask; + uint32_t request_ip; + uint32_t server_identifier; + uint32_t router_ip; + int lease_time; + uint8_t *options; + GList *request_parameters; +}; + +struct pg_dhcp_state { + struct pg_brick brick; + struct ether_addr **mac; + struct ether_addr mac_dhcp; + in_addr_t addr_net; + in_addr_t addr_broad; + int *check_ip; + int prefix; +}; + +struct pg_dhcp_config { + struct network_addr cidr; + struct ether_addr mac_dhcp; +}; + +static struct pg_brick_config *dhcp_config_new(const char *name, + const char *cidr, + struct ether_addr mac_dhcp) +{ + char cidrip[17]; + strcpy(cidrip, cidr); + struct pg_brick_config *config; + struct pg_dhcp_config *dhcp_config; + dhcp_config = g_new0(struct pg_dhcp_config, 1); + network_addr_t ip_cidr; + int ret = str_to_netaddr(cidrip, &ip_cidr); + if(ret == -1) + printf("error registering\n"); + dhcp_config->cidr = ip_cidr; + dhcp_config->mac_dhcp = mac_dhcp; + config = g_new0(struct pg_brick_config, 1); + config->brick_config = (void *) dhcp_config; + return pg_brick_config_init(config, name, 1, 1, PG_MONOPOLE); +} + +static int dhcp_burst(struct pg_brick *brick, enum pg_side from, + uint16_t edge_index, struct rte_mbuf **pkts, + uint64_t pkts_mask, struct pg_error **errp) +{ + struct pg_dhcp_state *state; + struct pg_brick_side *s = &brick->sides[pg_flip_side(from)]; + struct ether_addr *mac_pkts; + uint64_t it_mask; + struct rte_mbuf **pkt_offer; + uint64_t bit; + struct rte_mbuf *tmp; + uint16_t ether_type; + uint16_t i; + int j; + uint32_t hdrs_len = sizeof(struct ether_hdr) + + sizeof(struct ipv4_hdr) + + sizeof(struct udp_hdr); + + state = pg_brick_get_state(brick, struct pg_dhcp_state); + + it_mask = pkts_mask; + for (; it_mask;) { + j = 1; + pg_low_bit_iterate_full(it_mask, bit, i); + tmp = pkts[i]; + struct ether_hdr *eth = (struct ether_hdr *) + rte_pktmbuf_mtod(tmp, char *); + struct dhcp_messages_payload *dhcp_hdr = + (struct dhcp_messages_payload *) + rte_pktmbuf_mtod_offset(tmp, char *, hdrs_len); + ether_type = pg_utils_get_ether_type(tmp); + /* DHCP only manage IPv4 adressing + * Let non-ip packets (like ARP) pass. + */ + if (unlikely(ether_type != PG_BE_ETHER_TYPE_IPv4)) { + continue; + } + if (RTE_ETH_IS_IPV4_HDR(tmp->packet_type)) { + if (((tmp->packet_type) & RTE_PTYPE_L4_UDP)) { + if (is_discover(tmp)) { + while(state->check_ip[j] != 0) + j++; + uint32_t ip_offer = + state->addr_net + j; + pkt_offer = + pg_dhcp_packet_create(brick, 2, + eth->s_addr, ip_offer, 3600, + state->addr_net, + netmask(state->prefix)); + return pg_brick_burst(s->edge.link, + from, s->edge.pair_index, pkt_offer, + pkts_mask, errp); + } + if (is_request(tmp)) { + uint32_t index = dhcp_hdr->request_ip - + state->addr_net; + if (!state->check_ip[index]) + { + printf("check \n"); + pkt_offer = + pg_dhcp_packet_create(brick, 5, + eth->s_addr, + dhcp_hdr->request_ip, 3600, + state->addr_net, + netmask(state->prefix)); + mac_pkts = ð->s_addr; + state->mac[j] = mac_pkts; + return pg_brick_burst(s->edge.link, + from, s->edge.pair_index, pkt_offer, + pkts_mask, errp); + } + } + } + } + } + return pg_brick_burst(s->edge.link, from, + s->edge.pair_index, pkts, pkts_mask, errp); + +} + +static int dhcp_init(struct pg_brick *brick, + struct pg_brick_config *config, + struct pg_error **errp) +{ + struct pg_dhcp_state *state = pg_brick_get_state(brick, + struct pg_dhcp_state); + struct pg_dhcp_config *dhcp_config; + + state = pg_brick_get_state(brick, struct pg_dhcp_state); + + dhcp_config = (struct pg_dhcp_config *) config->brick_config; + state->mac = g_malloc0(pg_calcul_range_ip(dhcp_config->cidr) * + sizeof(struct ether_addr)); + state->mac_dhcp = dhcp_config->mac_dhcp; + state->addr_net = network(dhcp_config->cidr.addr, + dhcp_config->cidr.pfx); + state->addr_broad = broadcast(dhcp_config->cidr.addr, + dhcp_config->cidr.pfx); + state->check_ip = g_malloc0(pg_calcul_range_ip(dhcp_config->cidr) * + sizeof(bool)); + state->prefix = dhcp_config->cidr.pfx; + + for (int i = 0 ; i < pg_calcul_range_ip(dhcp_config->cidr); i++) { + state->check_ip[i] = 0; + } + /* initialize fast path */ + brick->burst = dhcp_burst; + + return 0; +} + +int pg_calcul_range_ip(network_addr_t ipcidr) { + return 1 << ((32 - (ipcidr.pfx - 1)) - 1); +} + +#define PG_PACKETS_TEST_DHCP(pkts, msg_type) \ + bool result = true; \ + uint8_t eth_dest[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};\ + uint8_t eth_src[6] = {0x00, 0x18, 0xb9, 0x56, 0x2e, 0x73}; \ + uint32_t ip_dest; \ + inet_pton(AF_INET, "255.255.255.255", &ip_dest); \ + uint32_t ip_src; \ + inet_pton(AF_INET, "0.0.0.0", &ip_src); \ + uint16_t udp_dest = 67; \ + uint16_t udp_src = 68; \ + uint8_t dhcp_mes_type = msg_type; \ + uint32_t hdrs_len = sizeof(struct ether_hdr) + \ + sizeof(struct ipv4_hdr) + \ + sizeof(struct udp_hdr); \ + struct ether_hdr *ethernet = (struct ether_hdr *) \ + rte_pktmbuf_mtod(pkts, char *);\ + struct ipv4_hdr *ip = (struct ipv4_hdr *) \ + pg_utils_get_l3(pkts); \ + struct udp_hdr *udp = (struct udp_hdr *) \ + pg_utils_get_l4(pkts); \ + struct dhcp_messages_payload *dhcp_hdr = \ + (struct dhcp_messages_payload *) \ + rte_pktmbuf_mtod_offset(pkts, char *, hdrs_len); \ + \ + if (eth_compare(ethernet->d_addr.addr_bytes, eth_dest) == false) \ + result = false; \ + if (eth_compare(ethernet->s_addr.addr_bytes, eth_src) == false) \ + result = false; \ + if (ip->dst_addr != ip_dest) \ + result = false; \ + if (ip->src_addr != ip_src) \ + result = false; \ + if (rte_be_to_cpu_16(udp->dst_port) != udp_dest) \ + result = false; \ + if (rte_be_to_cpu_16(udp->src_port) != udp_src) \ + result = false; \ + if (dhcp_hdr->dhcp_m_type != dhcp_mes_type) \ + result = false; \ + \ + return result; \ + + +bool is_discover(struct rte_mbuf *pkts) { + PG_PACKETS_TEST_DHCP(pkts, 1); +} + +bool is_request(struct rte_mbuf *pkts) { + PG_PACKETS_TEST_DHCP(pkts, 3); +} + +bool eth_compare(uint8_t eth1[6], uint8_t eth2[6]) { + bool result = true; + for(int i = 0; i < 6 ; i++) { + if (eth1[i] != eth2[i]) + result = false; + } + return result; +} + +struct rte_mbuf **pg_dhcp_packet_create(struct pg_brick *brick, + uint8_t msg_type, struct ether_addr mac, + uint32_t ip_offer, int bail, + uint32_t ip_router, uint32_t mask) +{ + struct rte_mbuf **pkts; + struct ether_addr eth_dst; + struct ether_addr eth_src; + struct dhcp_messages_payload dhcp_hdr; + char *tmp; + struct pg_dhcp_state *state; + + state = pg_brick_get_state(brick, struct pg_dhcp_state); + + pg_scan_ether_addr(ð_dst, "FF:FF:FF:FF:FF:FF"); + eth_src = state->mac_dhcp; + + pkts = pg_packets_append_ether(pg_packets_create(pg_mask_firsts(1)), + pg_mask_firsts(1), ð_src, ð_dst, + ETHER_TYPE_IPv4); + pg_packets_append_ipv4(pkts, pg_mask_firsts(1), state->addr_net, + 0xFFFFFFFF, sizeof(struct ipv4_hdr), 17); + pg_packets_append_udp(pkts, pg_mask_firsts(1), 68, 67, + sizeof(struct udp_hdr)); + + PG_FOREACH_BIT(pg_mask_firsts(1), j) { + if (!pkts[j]) + continue; + dhcp_hdr.mac_client = mac; + dhcp_hdr.dhcp_m_type = msg_type; + dhcp_hdr.subnet_mask = mask; + dhcp_hdr.yiaddr = ip_offer; + dhcp_hdr.router_ip = ip_router; + dhcp_hdr.lease_time = bail; + dhcp_hdr.server_identifier = state->addr_net; + tmp = rte_pktmbuf_append(pkts[j], + sizeof(struct dhcp_messages_payload)); + if (!tmp) + printf("error buffer\n"); + memcpy(tmp, &dhcp_hdr, sizeof(struct dhcp_messages_payload)); + } + + return pkts; +} + +struct pg_brick *pg_dhcp_new(const char *name, const char *cidr, + struct ether_addr mac_dhcp, + struct pg_error **errp) +{ + struct pg_brick_config *config; + struct pg_brick *ret; + + config = dhcp_config_new(name, cidr, mac_dhcp); + ret = pg_brick_new("dhcp", config, errp); + + pg_brick_config_free(config); + + return ret; +} + +static struct pg_brick_ops dhcp_ops = { + .name = "dhcp", + .state_size = sizeof(struct pg_dhcp_state), + + .init = dhcp_init, + + .unlink = pg_brick_generic_unlink, +}; + +pg_brick_register(dhcp, &dhcp_ops); diff --git a/src/iprange.c b/src/iprange.c new file mode 100644 index 000000000..b9b4cec71 --- /dev/null +++ b/src/iprange.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2003 Gabriel L. Somlo + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * To compile: + * on Linux: + * gcc -o iprange iprange.c -O2 -Wall + * on Solaris 8, Studio 8 CC: + * cc -xO5 -xarch=v8plusa -xdepend iprange.c -o iprange -lnsl -lresolv + * + * CHANGELOG: + * 2004-10-16 Paul Townsend (alpha alpha beta at purdue dot edu) + * - more general input/output formatting + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static char *PROG; + +/*------------------------------------------------------------------*/ +/* Set a bit to a given value (0 or 1); MSB is bit 1, LSB is bit 32 */ +/*------------------------------------------------------------------*/ +in_addr_t set_bit( in_addr_t addr, int bitno, int val ) { + + if ( val ) + return( addr | (1 << (32 - bitno)) ); + else + return( addr & ~(1 << (32 - bitno)) ); + +} /* set_bit() */ + + +/*--------------------------------------*/ +/* Compute netmask address given prefix */ +/*--------------------------------------*/ +in_addr_t netmask( int prefix ) { + + if ( prefix == 0 ) + return( ~((in_addr_t) -1) ); + else + return( ~((1 << (32 - prefix)) - 1) ); + +} /* netmask() */ + + +/*----------------------------------------------------*/ +/* Compute broadcast address given address and prefix */ +/*----------------------------------------------------*/ +in_addr_t broadcast( in_addr_t addr, int prefix ) { + + return( addr | ~netmask(prefix) ); + +} /* broadcast() */ + + +/*--------------------------------------------------*/ +/* Compute network address given address and prefix */ +/*--------------------------------------------------*/ +in_addr_t network( in_addr_t addr, int prefix ) { + + return( addr & netmask(prefix) ); + +} /* network() */ + + +/*------------------------------------------------*/ +/* Print out a 32-bit address in A.B.C.D/M format */ +/*------------------------------------------------*/ +void print_addr( in_addr_t addr, int prefix ) { + + struct in_addr in; + + in.s_addr = htonl( addr ); + if ( prefix < 32 ) + printf( "%s/%d\n", inet_ntoa(in), prefix ); + else + printf( "%s\n", inet_ntoa(in)); + +} /* print_addr() */ + +/*-----------------------------------------------------------*/ +/* Convert an A.B.C.D address into a 32-bit host-order value */ +/*-----------------------------------------------------------*/ +in_addr_t a_to_hl(const char *ipstr ) { + struct in_addr in; + if ( !inet_aton(ipstr, &in) ) { + fprintf( stderr, "%s: Invalid address %s!\n", PROG, ipstr ); + exit( 1 ); + } + + return( ntohl(in.s_addr) ); + +} /* a_to_hl() */ + + +/*-----------------------------------------------------------------*/ +/* convert a network address char string into a host-order network */ +/* address and an integer prefix value */ +/*-----------------------------------------------------------------*/ +int str_to_netaddr(const char *ipstr, network_addr_t *netaddr) { + long int prefix = 32; + char *prefixstr; + + if ( (prefixstr = strchr(ipstr, '/')) ) { + *prefixstr = '\0'; + prefixstr++; + prefix = strtol( prefixstr, (char **) NULL, 10 ); + if ((*prefixstr == '\0') || (prefix < 0) || (prefix > 32)) { + fprintf( stderr, "%s: Invalid prefix /%s...!\n", PROG, prefixstr ); + return -1; + } + } + else { + printf("no prefix"); + } + + netaddr->pfx = (int) prefix; + netaddr->addr = network( a_to_hl(ipstr), prefix ); + return 0; + +} /* str_to_netaddr() */ + +/*------------------------------------------------------*/ +/* Print out an address range in a.b.c.d-A.B.C.D format */ +/*------------------------------------------------------*/ +void print_addr_range( in_addr_t lo, in_addr_t hi ) { + + struct in_addr in; + + if ( lo != hi ) { + in.s_addr = htonl( lo ); + printf( "%s-", inet_ntoa(in) ); + } + + in.s_addr = htonl( hi ); + printf( "%s\n", inet_ntoa(in) ); + +} /* print_addr_range() */ + +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ + diff --git a/src/packets.c b/src/packets.c index 637d3ce21..9a45f500c 100644 --- a/src/packets.c +++ b/src/packets.c @@ -142,6 +142,7 @@ struct rte_mbuf **pg_packets_prepend_str(struct rte_mbuf **pkts, if (!tmp) \ return NULL; \ rte_memcpy(tmp, &ip_hdr, sizeof(ip_hdr)); \ + pkts[j]->packet_type += 16; \ pkts[j]->l3_len = sizeof(struct ipv4_hdr); \ } \ @@ -214,6 +215,8 @@ struct rte_mbuf **pg_packets_append_ipv6(struct rte_mbuf **pkts, tmp = rte_pktmbuf_##ops(pkts[j], sizeof(udp_hdr)); \ if (!tmp) \ return NULL; \ + pkts[j]->l4_len = sizeof(struct udp_hdr); \ + pkts[j]->packet_type += 512; \ memcpy(tmp, &udp_hdr, sizeof(udp_hdr)); \ } \ @@ -286,6 +289,7 @@ struct rte_mbuf **pg_packets_prepend_vxlan(struct rte_mbuf **pkts, if (!tmp) \ return NULL; \ rte_memcpy(tmp, ð_hdr, sizeof(eth_hdr)); \ + pkts[j]->packet_type += 1; \ pkts[j]->l2_len = sizeof(struct ether_hdr); \ } \ diff --git a/tests/dhcp/test.sh b/tests/dhcp/test.sh new file mode 100644 index 000000000..5c15edaca --- /dev/null +++ b/tests/dhcp/test.sh @@ -0,0 +1,2 @@ +#!/bin/sh +sudo ./tests-dhcp -c1 -n1 --socket-mem 64 --no-shconf diff --git a/tests/dhcp/tests.c b/tests/dhcp/tests.c new file mode 100644 index 000000000..3dae20f20 --- /dev/null +++ b/tests/dhcp/tests.c @@ -0,0 +1,303 @@ + /* Copyright 2017 + * + * This file is part of Packetgraph. + * + * Packetgraph is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as published + * by the Free Software Foundation. + * + * Packetgraph 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 Packetgraph. If not, see . + */ + +#include +#include + +#define __FAVOR_BSD +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include "utils/tests.h" +#include +#include +#include "brick-int.h" +#include "collect.h" +#include "fail.h" +#include "packetsgen.h" +#include "packets.h" +#include "utils/mempool.h" +#include "utils/qemu.h" +#include "utils/mac.h" +#include "utils/bitmask.h" + +extern char * ether_ntoa(struct ether_addr *e); +static uint16_t ssh_port_id = 65000; + +struct dhcp_messages_payload { + uint8_t op; + uint8_t htype; + uint8_t hlen; + uint8_t hops; + uint32_t xid; + uint16_t secs; + uint16_t flags; + uint32_t ciaddr; + uint32_t yiaddr; + uint32_t siaddr; + uint32_t giaddr; + struct ether_addr mac_client; + char * server_name; + char * file; + uint8_t dhcp_m_type; + struct ether_addr client_id; + uint32_t subnet_mask; + uint32_t request_ip; + uint32_t server_identifier; + uint32_t router_ip; + int lease_time; + uint8_t *options; + GList *request_parameters; +}; + +struct pg_dhcp_state { + struct pg_brick brick; + struct ether_addr **mac; + in_addr_t addr_net; + in_addr_t addr_broad; + int *check_ip; + int prefix; +}; + +static void test_dhcp_discover(void) +{ + struct pg_brick *dhcp; + struct pg_brick *collect; + struct ether_addr eth_dst; + struct ether_addr eth_src; + struct ether_addr eth_dhcp; + struct rte_mbuf **pkts; + struct rte_mbuf **sended_pkts; + struct dhcp_messages_payload dhcp_hdr; + char *tmp; + uint64_t nb_packets = pg_mask_firsts(1); + uint8_t dhcp_mes_type = 2; + struct pg_error *error = NULL; + + pg_scan_ether_addr(ð_dst, "FF:FF:FF:FF:FF:FF"); + pg_scan_ether_addr(ð_src, "00:18:b9:56:2e:73"); + pg_scan_ether_addr(ð_dhcp, "44:82:c9:2b:a2:37"); + pkts = pg_packets_append_ether(pg_packets_create(nb_packets), + nb_packets, ð_src, ð_dst, + ETHER_TYPE_IPv4); + pg_packets_append_ipv4(pkts, nb_packets, 0x00000000, 0xFFFFFFFF, + sizeof(struct ipv4_hdr), 17); + + pg_packets_append_udp(pkts, nb_packets, 68, 67, sizeof(struct udp_hdr)); + + PG_FOREACH_BIT(nb_packets, j) { + if (!pkts[j]) + continue; + dhcp_hdr.mac_client = eth_src; + dhcp_hdr.dhcp_m_type = 1; + dhcp_hdr.client_id = eth_src; + tmp = rte_pktmbuf_append(pkts[j], sizeof(struct dhcp_messages_payload)); + if (!tmp) + printf("error buffer\n"); + memcpy(tmp, &dhcp_hdr, sizeof(struct dhcp_messages_payload)); + } + + collect = pg_collect_new("collect", &error); + g_assert(!error); + + dhcp = pg_dhcp_new("dhcp", "192.54.30.200/24", eth_dhcp, &error); + g_assert(!error); + + pg_brick_link(dhcp, collect, &error); + g_assert(!error); + + pg_brick_burst_to_east(dhcp, 0, pkts, nb_packets, &error); + g_assert(!error); + + sended_pkts = pg_brick_west_burst_get(collect, &nb_packets, &error); + g_assert(!error); + + PG_FOREACH_BIT(nb_packets, j) { + if (!pkts[j]) + continue; + uint32_t hdrs_len = sizeof(struct ether_hdr) + + sizeof(struct ipv4_hdr) + + sizeof(struct udp_hdr); + struct dhcp_messages_payload *dhcp_hdr_sended = + (struct dhcp_messages_payload *) + rte_pktmbuf_mtod_offset(sended_pkts[j], char *, hdrs_len); + g_assert(dhcp_hdr_sended->dhcp_m_type == dhcp_mes_type); + } + + pg_brick_destroy(collect); + pg_brick_destroy(dhcp); + pg_packets_free(pkts, pg_mask_firsts(1)); + g_free(pkts); +} + +static void test_dhcp_packets_registering(void) +{ + struct pg_brick *dhcp; + struct pg_brick *packets_gen; + struct rte_mbuf **pkts; + struct pg_error *error = NULL; + struct ether_addr eth_dst; + struct ether_addr eth_src; + struct ether_addr eth_dhcp; + struct dhcp_messages_payload dhcp_hdr; + char *tmp; + uint16_t nb_packets = 1; + + pg_scan_ether_addr(ð_dst, "FF:FF:FF:FF:FF:FF"); + pg_scan_ether_addr(ð_src, "00:18:b9:56:2e:73"); + pg_scan_ether_addr(ð_dhcp, "44:82:c9:2b:a2:37"); + pkts = pg_packets_append_ether(pg_packets_create(pg_mask_firsts(1)), + pg_mask_firsts(1), ð_src, ð_dst, + ETHER_TYPE_IPv4); + pg_packets_append_ipv4(pkts, pg_mask_firsts(1), 0x00000000, 0xFFFFFFFF, + sizeof(struct ipv4_hdr), 17); + + pg_packets_append_udp(pkts, pg_mask_firsts(1), 68, 67, sizeof(struct udp_hdr)); + + PG_FOREACH_BIT(pg_mask_firsts(1), j) { + if (!pkts[j]) + continue; + dhcp_hdr.mac_client = eth_src; + dhcp_hdr.dhcp_m_type = 3; + dhcp_hdr.client_id = eth_src; + dhcp_hdr.request_ip = 0xC0361E01; + tmp = rte_pktmbuf_append(pkts[j], sizeof(struct dhcp_messages_payload)); + if (!tmp) + printf("error buffer\n"); + memcpy(tmp, &dhcp_hdr, sizeof(struct dhcp_messages_payload)); + } + + packets_gen = pg_packetsgen_new("packetsgen", 1, 1, PG_EAST_SIDE, pkts, + 1, &error); + g_assert(!error); + + dhcp = pg_dhcp_new("dhcp", "192.54.30.200/24", eth_dhcp, &error); + g_assert(!error); + + pg_brick_link(packets_gen, dhcp, &error); + g_assert(!error); + + pg_brick_poll(packets_gen, &nb_packets, &error); + g_assert(!error); + + struct pg_dhcp_state *state = pg_brick_get_state(dhcp, + struct pg_dhcp_state); + int size = sizeof(state->mac) / sizeof(struct ether_addr); + for(int i = 1; i <= size ; i++) { + printf("%i \n", i); + if (state->mac[i]) { + printf("%s\n", ether_ntoa(state->mac[i])); + } + else { + printf("No Mac assigned\n"); + } + } + + pg_brick_destroy(packets_gen); + pg_brick_destroy(dhcp); + pg_packets_free(pkts, pg_mask_firsts(1)); + g_free(pkts); + +} +/* +static void test_adressing_vm(void) +{ + struct pg_brick *dhcp, *vhost, *print, *collect; + const char *socket_path; + struct ether_addr mac_vm; + struct ether_addr mac_dhcp; + pg_scan_ether_addr(&mac_vm, "42:18:b9:56:2e:73"); + pg_scan_ether_addr(&mac_dhcp, "55:27:c9:ea:9d:36"); + struct pg_error *error = NULL; + int ret, qemu_pid; + + ret = pg_vhost_start("/tmp", &error); + g_assert(ret == 0); + g_assert(!error); + + vhost = pg_vhost_new("vhost", PG_VHOST_USER_DEQUEUE_ZERO_COPY, + &error); + g_assert(!error); + g_assert(vhost); + + dhcp = pg_dhcp_new("dhcp", "192.54.30.200/24", mac_dhcp, &error); + g_assert(!error); + g_assert(dhcp); + + collect = pg_collect_new("collect", &error); + g_assert(!error); + g_assert(collect); + + print = pg_print_new("My print", NULL, PG_PRINT_FLAG_MAX, NULL, + &error); + g_assert(!error); + g_assert(print); + + pg_brick_link(collect, vhost, &error); + g_assert(!error); + + socket_path = pg_vhost_socket_path(vhost, &error); + g_assert(!error); + g_assert(socket_path); + + qemu_pid = pg_util_spawn_qemu(socket_path, NULL, mac_vm, NULL, + glob_vm_path, glob_vm_key_path, + glob_hugepages_path, &error); + + g_assert(!error); + g_assert(qemu_pid); + +# define SSH(c) \ + g_assert(pg_util_ssh("localhost", ssh_port_id, \ + glob_vm_key_path, c) == 0) + SSH("dhclient ens4"); +# undef SSH + + pg_util_stop_qemu(qemu_pid, qemu_exit_signal); + +} +*/ +static void test_dhcp(void) +{ + pg_test_add_func("/dhcp/discover\n", + test_dhcp_discover); + pg_test_add_func("/dhcp/packets_registering\n", + test_dhcp_packets_registering); +} + +int main(int argc, char **argv) +{ + /* tests in the same order as the header function declarations */ + g_test_init(&argc, &argv, NULL); + + /* initialize packetgraph */ + g_assert(pg_start(argc, argv) >= 0); + + test_dhcp(); + int r = g_test_run(); + + pg_stop(); + return r; +}