Skip to content
Snippets Groups Projects
Commit 77954df7 authored by Neal Cardwell's avatar Neal Cardwell
Browse files

packetdrill: initial commit

Initial commit for the packetdrill network stack testing tool:

  https://code.google.com/p/packetdrill/



Signed-off-by: default avatarNeal Cardwell <ncardwell@google.com>
parents
No related branches found
No related tags found
No related merge requests found
Showing
with 2510 additions and 0 deletions
/*
* Copyright 2013 Google Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/*
* Author: ncardwell@google.com (Neal Cardwell)
*
* FreeBSD does not have an fmemopen(), so we roll our own minimalist
* implementation here.
*/
#ifndef __FMEMOPEN_H__
#define __FMEMOPEN_H__
#ifndef HAVE_FMEMOPEN
#include "types.h"
extern FILE *fmemopen(char *buf, size_t size, const char *mode);
#endif /* HAVE_FMEMOPEN */
#endif /* __FMEMOPEN_H__ */
/*
* Copyright 2013 Google Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/*---------------------------------------------------------------------------
* From public domain code at:
* http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp
*/
/*---------------------------------------------------------------------------
* MurmurHash3 was written by Austin Appleby, and is placed in the public
* domain. The author hereby disclaims copyright to this source code.
*
* Note - The x86 and x64 versions do _not_ produce the same results, as the
* algorithms are optimized for their respective platforms. You can still
* compile and run any of them on any platform, but your performance with the
* non-native version will be less than optimal.
*/
#include "hash.h"
/*---------------------------------------------------------------------------
* Platform-specific functions and macros
*/
static __always_inline u32 rotl32(u32 x, s8 r)
{
return (x << r) | (x >> (32 - r));
}
static __always_inline u64 rotl64(u64 x, s8 r)
{
return (x << r) | (x >> (64 - r));
}
#define ROTL32(x, y) rotl32(x, y)
#define ROTL64(x, y) rotl64(x, y)
#define BIG_CONSTANT(x) (x##LLU)
/*---------------------------------------------------------------------------
* Block read - if your platform needs to do endian-swapping or can only
* handle aligned reads, do the conversion here
*/
static __always_inline u32 getblock_32(const u32 *p, int i)
{
return p[i];
}
static __always_inline u64 getblock_64(const u64 *p, int i)
{
return p[i];
}
/*---------------------------------------------------------------------------
* Finalization mix - force all bits of a hash block to avalanche
*/
static __always_inline u32 fmix_32(u32 h)
{
h ^= h >> 16;
h *= 0x85ebca6b;
h ^= h >> 13;
h *= 0xc2b2ae35;
h ^= h >> 16;
return h;
}
/*---------*/
static __always_inline u64 fmix_64(u64 k)
{
k ^= k >> 33;
k *= BIG_CONSTANT(0xff51afd7ed558ccd);
k ^= k >> 33;
k *= BIG_CONSTANT(0xc4ceb9fe1a85ec53);
k ^= k >> 33;
return k;
}
/*---------------------------------------------------------------------------*/
void MurmurHash3_x86_32(const void *key, int len, u32 seed, void *out)
{
const u8 *data = (const u8 *)key;
const int nblocks = len / 4;
u32 h1 = seed;
u32 c1 = 0xcc9e2d51;
u32 c2 = 0x1b873593;
/*---------*/
/* body */
const u32 *blocks = (const u32 *)(data + nblocks * 4);
int i;
for (i = -nblocks; i; i++) {
u32 k1 = getblock_32(blocks, i);
k1 *= c1;
k1 = ROTL32(k1, 15);
k1 *= c2;
h1 ^= k1;
h1 = ROTL32(h1, 13);
h1 = h1 * 5 + 0xe6546b64;
}
/*---------*/
/* tail */
const u8 *tail = (const u8 *)(data + nblocks * 4);
u32 k1 = 0;
switch (len & 3) {
case 3:
k1 ^= tail[2] << 16;
case 2:
k1 ^= tail[1] << 8;
case 1:
k1 ^= tail[0];
k1 *= c1;
k1 = ROTL32(k1, 15);
k1 *= c2;
h1 ^= k1;
};
/*---------*/
/* finalization */
h1 ^= len;
h1 = fmix_32(h1);
*(u32 *) out = h1;
}
/*---------------------------------------------------------------------------*/
void MurmurHash3_x86_128(const void *key, const int len, u32 seed, void *out)
{
const u8 *data = (const u8 *)key;
const int nblocks = len / 16;
u32 h1 = seed;
u32 h2 = seed;
u32 h3 = seed;
u32 h4 = seed;
u32 c1 = 0x239b961b;
u32 c2 = 0xab0e9789;
u32 c3 = 0x38b34ae5;
u32 c4 = 0xa1e38b93;
/*---------*/
/* body */
const u32 *blocks = (const u32 *)(data + nblocks * 16);
int i;
for (i = -nblocks; i; i++) {
u32 k1 = getblock_32(blocks, i * 4 + 0);
u32 k2 = getblock_32(blocks, i * 4 + 1);
u32 k3 = getblock_32(blocks, i * 4 + 2);
u32 k4 = getblock_32(blocks, i * 4 + 3);
k1 *= c1;
k1 = ROTL32(k1, 15);
k1 *= c2;
h1 ^= k1;
h1 = ROTL32(h1, 19);
h1 += h2;
h1 = h1 * 5 + 0x561ccd1b;
k2 *= c2;
k2 = ROTL32(k2, 16);
k2 *= c3;
h2 ^= k2;
h2 = ROTL32(h2, 17);
h2 += h3;
h2 = h2 * 5 + 0x0bcaa747;
k3 *= c3;
k3 = ROTL32(k3, 17);
k3 *= c4;
h3 ^= k3;
h3 = ROTL32(h3, 15);
h3 += h4;
h3 = h3 * 5 + 0x96cd1c35;
k4 *= c4;
k4 = ROTL32(k4, 18);
k4 *= c1;
h4 ^= k4;
h4 = ROTL32(h4, 13);
h4 += h1;
h4 = h4 * 5 + 0x32ac3b17;
}
/*---------*/
/* tail */
const u8 *tail = (const u8 *)(data + nblocks * 16);
u32 k1 = 0;
u32 k2 = 0;
u32 k3 = 0;
u32 k4 = 0;
switch (len & 15) {
case 15:
k4 ^= tail[14] << 16;
case 14:
k4 ^= tail[13] << 8;
case 13:
k4 ^= tail[12] << 0;
k4 *= c4;
k4 = ROTL32(k4, 18);
k4 *= c1;
h4 ^= k4;
case 12:
k3 ^= tail[11] << 24;
case 11:
k3 ^= tail[10] << 16;
case 10:
k3 ^= tail[9] << 8;
case 9:
k3 ^= tail[8] << 0;
k3 *= c3;
k3 = ROTL32(k3, 17);
k3 *= c4;
h3 ^= k3;
case 8:
k2 ^= tail[7] << 24;
case 7:
k2 ^= tail[6] << 16;
case 6:
k2 ^= tail[5] << 8;
case 5:
k2 ^= tail[4] << 0;
k2 *= c2;
k2 = ROTL32(k2, 16);
k2 *= c3;
h2 ^= k2;
case 4:
k1 ^= tail[3] << 24;
case 3:
k1 ^= tail[2] << 16;
case 2:
k1 ^= tail[1] << 8;
case 1:
k1 ^= tail[0] << 0;
k1 *= c1;
k1 = ROTL32(k1, 15);
k1 *= c2;
h1 ^= k1;
};
/*---------*/
/* finalization */
h1 ^= len;
h2 ^= len;
h3 ^= len;
h4 ^= len;
h1 += h2;
h1 += h3;
h1 += h4;
h2 += h1;
h3 += h1;
h4 += h1;
h1 = fmix_32(h1);
h2 = fmix_32(h2);
h3 = fmix_32(h3);
h4 = fmix_32(h4);
h1 += h2;
h1 += h3;
h1 += h4;
h2 += h1;
h3 += h1;
h4 += h1;
((u32 *) out)[0] = h1;
((u32 *) out)[1] = h2;
((u32 *) out)[2] = h3;
((u32 *) out)[3] = h4;
}
/*---------------------------------------------------------------------------*/
void MurmurHash3_x64_128(const void *key, const int len,
const u32 seed, void *out)
{
const u8 *data = (const u8 *)key;
const int nblocks = len / 16;
u64 h1 = seed;
u64 h2 = seed;
u64 c1 = BIG_CONSTANT(0x87c37b91114253d5);
u64 c2 = BIG_CONSTANT(0x4cf5ad432745937f);
/*---------*/
/* body */
const u64 *blocks = (const u64 *)(data);
int i;
for (i = 0; i < nblocks; i++) {
u64 k1 = getblock_64(blocks, i * 2 + 0);
u64 k2 = getblock_64(blocks, i * 2 + 1);
k1 *= c1;
k1 = ROTL64(k1, 31);
k1 *= c2;
h1 ^= k1;
h1 = ROTL64(h1, 27);
h1 += h2;
h1 = h1 * 5 + 0x52dce729;
k2 *= c2;
k2 = ROTL64(k2, 33);
k2 *= c1;
h2 ^= k2;
h2 = ROTL64(h2, 31);
h2 += h1;
h2 = h2 * 5 + 0x38495ab5;
}
/*---------*/
/* tail */
const u8 *tail = (const u8 *)(data + nblocks * 16);
u64 k1 = 0;
u64 k2 = 0;
switch (len & 15) {
case 15:
k2 ^= (u64) (tail[14]) << 48;
case 14:
k2 ^= (u64) (tail[13]) << 40;
case 13:
k2 ^= (u64) (tail[12]) << 32;
case 12:
k2 ^= (u64) (tail[11]) << 24;
case 11:
k2 ^= (u64) (tail[10]) << 16;
case 10:
k2 ^= (u64) (tail[9]) << 8;
case 9:
k2 ^= (u64) (tail[8]) << 0;
k2 *= c2;
k2 = ROTL64(k2, 33);
k2 *= c1;
h2 ^= k2;
case 8:
k1 ^= (u64) (tail[7]) << 56;
case 7:
k1 ^= (u64) (tail[6]) << 48;
case 6:
k1 ^= (u64) (tail[5]) << 40;
case 5:
k1 ^= (u64) (tail[4]) << 32;
case 4:
k1 ^= (u64) (tail[3]) << 24;
case 3:
k1 ^= (u64) (tail[2]) << 16;
case 2:
k1 ^= (u64) (tail[1]) << 8;
case 1:
k1 ^= (u64) (tail[0]) << 0;
k1 *= c1;
k1 = ROTL64(k1, 31);
k1 *= c2;
h1 ^= k1;
};
/*---------*/
/* finalization */
h1 ^= len;
h2 ^= len;
h1 += h2;
h2 += h1;
h1 = fmix_64(h1);
h2 = fmix_64(h2);
h1 += h2;
h2 += h1;
((u64 *) out)[0] = h1;
((u64 *) out)[1] = h2;
}
/*---------------------------------------------------------------------------*/
/*
* Copyright 2013 Google Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/* From: http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.h */
/*---------------------------------------------------------------------------
* MurmurHash3 was written by Austin Appleby, and is placed in the public
* domain. The author hereby disclaims copyright to this source code.
*/
#ifndef _MURMURHASH3_H_
#define _MURMURHASH3_H_
#include "types.h"
#include <stdint.h>
/*---------------------------------------------------------------------------*/
void MurmurHash3_x86_32(const void *key, int len, u32 seed, void *out);
void MurmurHash3_x86_128(const void *key, int len, u32 seed, void *out);
void MurmurHash3_x64_128(const void *key, int len, u32 seed, void *out);
/*---------------------------------------------------------------------------*/
#endif /* _MURMURHASH3_H_ */
/*
* Copyright 2013 Google Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/*
* Author: ncardwell@google.com (Neal Cardwell)
*
* Implementation for a simple hash map mapping u32 keys to u32 values.
*/
#include "hash_map.h"
#include <stdlib.h>
#include <string.h>
#include "hash.h"
static const size_t MAX_BUCKETS = 1ULL << 30; /* max 1B buckets */
/* Hash a key. We use the fast, public-domain MurmurHash3.*/
static inline size_t hash_key(u32 key)
{
u32 hash;
MurmurHash3_x86_32(&key, sizeof(key), 0, &hash);
return hash;
}
/* Find the bucket number for a key. */
static inline size_t hash_bucket_num(const struct hash_map *map, u32 key)
{
size_t bucket_num = hash_key(key) & map->bucket_mask;
return bucket_num;
}
/* Try to find the smallest bucket count that is a power of 2 and is
* greater than the given number of keys.
*/
static inline size_t hash_map_pick_bucket_count(size_t num_keys)
{
size_t buckets = 1;
while ((buckets < num_keys) && (buckets < MAX_BUCKETS))
buckets <<= 1;
return buckets;
}
struct hash_map *hash_map_new(size_t num_keys)
{
struct hash_map *map = calloc(1, sizeof(struct hash_map));
map->num_buckets = hash_map_pick_bucket_count(num_keys);
map->bucket_mask = map->num_buckets - 1;
map->buckets = calloc(map->num_buckets, sizeof(struct hash_node *));
return map;
}
void hash_map_free(struct hash_map *map)
{
/* Walk through the buckets and free nodes. */
int bucket_num;
for (bucket_num = 0; bucket_num < map->num_buckets; ++bucket_num) {
struct hash_node *node = NULL;
struct hash_node *next = NULL;
for (node = map->buckets[bucket_num]; node != NULL;
node = next) {
next = node->next;
free(node);
}
}
free(map->buckets);
memset(map, 0, sizeof(*map)); /* paranoia to help catch bugs */
free(map);
}
/* Link the given node into the correct bucket linked list in the hash map. */
static void hash_map_link(struct hash_map *map,
struct hash_node *node)
{
const size_t bucket_num = hash_bucket_num(map, node->key);
node->next = map->buckets[bucket_num];
map->buckets[bucket_num] = node;
}
/* Create a new array of buckets that's twice the size of the current
* array. Then Walk through the old buckets and move all the nodes to
* the new buckets.
*/
static void hash_map_grow(struct hash_map *map)
{
const size_t old_num_buckets = map->num_buckets;
map->num_buckets *= 2;
map->bucket_mask = map->num_buckets - 1;
struct hash_node **old_buckets = map->buckets;
map->buckets = calloc(map->num_buckets, sizeof(struct hash_node *));
size_t old_bucket_num = 0;
for (old_bucket_num = 0; old_bucket_num < old_num_buckets;
++old_bucket_num) {
struct hash_node *node = NULL;
struct hash_node *next = NULL;
for (node = old_buckets[old_bucket_num]; node != NULL;
node = next) {
next = node->next;
hash_map_link(map, node);
}
}
free(old_buckets);
}
/* Insert a new node in the hash map, first growing the map if needed. */
static void hash_map_insert(struct hash_map *map, u32 key, u32 value)
{
/* To keep things simple, we target a load factor of 1.0. */
if ((map->num_keys >= map->num_buckets) &&
(map->num_buckets < MAX_BUCKETS)) {
hash_map_grow(map);
}
++map->num_keys;
struct hash_node *node = calloc(1, sizeof(struct hash_node));
node->key = key;
node->value = value;
hash_map_link(map, node);
}
void hash_map_set(struct hash_map *map, u32 key, u32 value)
{
const size_t bucket_num = hash_bucket_num(map, key);
struct hash_node *node = NULL;
for (node = map->buckets[bucket_num]; node != NULL; node = node->next) {
if (node->key == key) {
node->value = value;
return;
}
}
hash_map_insert(map, key, value);
}
bool hash_map_get(const struct hash_map *map, u32 key, u32 *value)
{
const size_t bucket_num = hash_bucket_num(map, key);
struct hash_node *node = NULL;
for (node = map->buckets[bucket_num]; node != NULL; node = node->next) {
if (node->key == key) {
*value = node->value;
return true;
}
}
return false;
}
/*
* Copyright 2013 Google Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/*
* Author: ncardwell@google.com (Neal Cardwell)
*
* Interface and data structure declarations for a simple hash map
* mapping u32 keys to u32 values.
*/
#ifndef __HASH_MAP_H__
#define __HASH_MAP_H__
#include "types.h"
/* Node for hash table buckets; maps u32 key to u32 value. */
struct hash_node {
u32 key;
u32 value;
struct hash_node *next;
};
/* Hash map mapping u32 to u32. */
struct hash_map {
size_t num_keys; /* number of keys */
size_t num_buckets; /* number of buckets (a power of 2) */
size_t bucket_mask; /* bit mask to find bucket number */
struct hash_node **buckets; /* array of hash buckets */
};
extern struct hash_map *hash_map_new(size_t num_keys);
extern void hash_map_free(struct hash_map *map);
extern void hash_map_set(struct hash_map *map,
u32 key, u32 value);
extern bool hash_map_get(const struct hash_map *map,
u32 key, u32 *value);
#endif /* __HASH_MAP_H__ */
/*
* Copyright 2013 Google Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/*
* Author: ncardwell@google.com (Neal Cardwell)
*
* Our own ICMPv4 header declarations, so we have something that's
* portable and somewhat more readable than a typical system header
* file.
*/
#ifndef __ICMP_HEADERS_H__
#define __ICMP_HEADERS_H__
#include "types.h"
/* Most ICMPv6 message types include a copy of the outbound IP header
* and the first few bytes inside, to allow the receiver to demux by
* TCP/UDP port. The following constant specifies the number of bytes
* of TCP header that we will echo. We echo 8 bytes because that
* is the minimum number of bytes that the Linux TCP stack needs to
* read the source and destination TCP port and TCP sequence number,
* which it needs to properly demux an incoming ICMP packet to a
* specific TCP connection.
*/
#define ICMP_ECHO_BYTES 8
struct icmpv4 {
__u8 type;
__u8 code;
__sum16 checksum;
union {
struct {
__be16 id;
__be16 sequence;
} echo;
__be32 gateway;
struct {
__be16 unused;
__be16 mtu;
} frag; /* PMTU discovery, RFC 1191 */
} message;
};
/* Our own ICMP definitions, since the names vary between platforms. */
/* ICMPv4 types */
#define ICMP_ECHOREPLY 0
#define ICMP_DEST_UNREACH 3
#define ICMP_SOURCE_QUENCH 4
#define ICMP_REDIRECT 5
#define ICMP_ECHO 8
#define ICMP_TIME_EXCEEDED 11
#define ICMP_PARAMETERPROB 12
#define ICMP_TIMESTAMP 13
#define ICMP_TIMESTAMPREPLY 14
#define ICMP_INFO_REQUEST 15
#define ICMP_INFO_REPLY 16
#define ICMP_ADDRESS 17
#define ICMP_ADDRESSREPLY 18
#define NR_ICMP_TYPES 18
/* Codes for ICMP_DEST_UNREACH */
#define ICMP_NET_UNREACH 0
#define ICMP_HOST_UNREACH 1
#define ICMP_PROT_UNREACH 2
#define ICMP_PORT_UNREACH 3
#define ICMP_FRAG_NEEDED 4
#define ICMP_SR_FAILED 5
#define ICMP_NET_UNKNOWN 6
#define ICMP_HOST_UNKNOWN 7
#define ICMP_HOST_ISOLATED 8
#define ICMP_NET_ANO 9
#define ICMP_HOST_ANO 10
#define ICMP_NET_UNR_TOS 11
#define ICMP_HOST_UNR_TOS 12
#define ICMP_PKT_FILTERED 13
#define ICMP_PREC_VIOLATION 14
#define ICMP_PREC_CUTOFF 15
#define NR_ICMP_UNREACH 15
#endif /* __ICMP_HEADERS_H__ */
/*
* Copyright 2013 Google Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/*
* Author: ncardwell@google.com (Neal Cardwell)
*
* Implementation for module for formatting ICMP packets.
*/
#include "icmp_packet.h"
#include "icmp.h"
#include "icmpv6.h"
#include "ip_packet.h"
/* A table entry mapping an ICMP code string to byte. */
struct icmp_code_info {
u8 code_byte; /* type byte on the wire */
const char *code_string; /* human-readable code */
};
/* A table entry mapping an ICMP type string to byte and code table. */
struct icmp_type_info {
u8 type_byte; /* type byte on the wire */
const char *type_string; /* human-readable type */
const struct icmp_code_info *code_table; /* codes for this type */
};
/* Values for the 'code' byte of an IPv4 ICMP_DEST_UNREACH header (RFC 1700). */
struct icmp_code_info icmpv4_unreachable_codes[] = {
{ ICMP_NET_UNREACH, "net_unreachable" },
{ ICMP_HOST_UNREACH, "host_unreachable" },
{ ICMP_PROT_UNREACH, "protocol_unreachable" },
{ ICMP_PORT_UNREACH, "port_unreachable" },
{ ICMP_FRAG_NEEDED, "frag_needed" },
{ ICMP_SR_FAILED, "source_route_failed" },
{ ICMP_NET_UNKNOWN, "net_unknown" },
{ ICMP_HOST_UNKNOWN, "host_unknown" },
{ ICMP_HOST_ISOLATED, "source_host_isolated" },
{ ICMP_NET_ANO, "net_prohibited" },
{ ICMP_HOST_ANO, "host_prohibited" },
{ ICMP_NET_UNR_TOS, "net_unreachable_for_tos" },
{ ICMP_HOST_UNR_TOS, "host_unreachable_for_tos" },
{ ICMP_PKT_FILTERED, "packet_filtered" },
{ ICMP_PREC_VIOLATION, "precedence_violation" },
{ ICMP_PREC_CUTOFF, "precedence_cutoff" },
{ 0, NULL },
};
/* Information about the supported types of ICMPv4 header (RFC 1700). */
struct icmp_type_info icmpv4_types[] = {
{ ICMP_ECHOREPLY, "echo_reply" },
{ ICMP_DEST_UNREACH, "unreachable", icmpv4_unreachable_codes },
{ ICMP_SOURCE_QUENCH, "source_quench" },
{ ICMP_REDIRECT, "redirect" },
{ ICMP_ECHO, "echo_request" },
{ ICMP_TIME_EXCEEDED, "time_exceeded" },
{ ICMP_PARAMETERPROB, "parameter_problem" },
{ ICMP_TIMESTAMP, "timestamp_request" },
{ ICMP_TIMESTAMPREPLY, "timestamp_reply" },
{ ICMP_INFO_REQUEST, "information_request" },
{ ICMP_INFO_REPLY, "information_reply" },
{ ICMP_ADDRESS, "address_mask_request" },
{ ICMP_ADDRESSREPLY, "address_mask_reply" },
{ 0, NULL, NULL },
};
/* Values for the 'code' byte of an ICMPV6_DEST_UNREACH header (RFC 2463). */
struct icmp_code_info icmpv6_unreachable_codes[] = {
{ ICMP_NET_UNREACH, "net_unreachable" },
{ ICMPV6_NOROUTE, "no_route" },
{ ICMPV6_ADM_PROHIBITED, "admin_prohibited" },
{ ICMPV6_NOT_NEIGHBOUR, "not_neighbour" },
{ ICMPV6_ADDR_UNREACH, "address_unreachable" },
{ ICMPV6_PORT_UNREACH, "port_unreachable" },
{ 0, NULL },
};
/* Values for the 'code' byte of an ICMPV6_TIME_EXCEED header (RFC 2463). */
struct icmp_code_info icmpv6_time_exceed_codes[] = {
{ ICMPV6_EXC_HOPLIMIT, "exceeded_hop_limit" },
{ ICMPV6_EXC_FRAGTIME, "exceeded_frag_time" },
{ 0, NULL },
};
/* Values for the 'code' byte of an ICMPV6_PARAMPROB header (RFC 2463). */
struct icmp_code_info icmpv6_paramprob_codes[] = {
{ ICMPV6_HDR_FIELD, "header_field" },
{ ICMPV6_UNK_NEXTHDR, "unknown_next_header" },
{ ICMPV6_UNK_OPTION, "unknown_option" },
{ 0, NULL },
};
/* Information about the supported types of ICMPv6 header (RFC 2463). */
struct icmp_type_info icmpv6_types[] = {
{ ICMPV6_DEST_UNREACH, "unreachable", icmpv6_unreachable_codes },
{ ICMPV6_PKT_TOOBIG, "packet_too_big" },
{ ICMPV6_TIME_EXCEED, "time_exceeded", icmpv6_time_exceed_codes },
{ ICMPV6_PARAMPROB, "parameter_problem", icmpv6_paramprob_codes },
{ 0, NULL, NULL },
};
/* Return the ICMP protocol number for the given address family. */
static int icmp_protocol(int address_family)
{
if (address_family == AF_INET)
return IPPROTO_ICMP;
else if (address_family == AF_INET6)
return IPPROTO_ICMPV6;
else
assert(!"bad ip version");
return 0;
}
/* Return the length in bytes of the ICMP header. */
static int icmp_header_len(int address_family)
{
if (address_family == AF_INET)
return sizeof(struct icmpv4);
else if (address_family == AF_INET6)
return sizeof(struct icmpv6);
else
assert(!"bad ip version");
return 0;
}
/* Fill in ICMPv4 header fields. */
static int set_icmpv4_header(struct icmpv4 *icmpv4,
u8 type, u8 code, s64 mtu, char **error)
{
icmpv4->type = type;
icmpv4->code = code;
icmpv4->checksum = htons(0);
if (mtu >= 0) {
if ((type != ICMP_DEST_UNREACH) || (code != ICMP_FRAG_NEEDED)) {
asprintf(error,
"ICMPv4 MTU is only valid for "
"unreachable-frag_needed");
return STATUS_ERR;
}
if (!is_valid_u16(mtu)) {
asprintf(error, "ICMPv4 MTU out of 16-bit range");
return STATUS_ERR;
}
icmpv4->message.frag.mtu = htons(mtu);
}
return STATUS_OK;
}
/* Fill in ICMPv4 header fields. */
static int set_icmpv6_header(struct icmpv6 *icmpv6,
u8 type, u8 code, s64 mtu, char **error)
{
icmpv6->type = type;
icmpv6->code = code;
icmpv6->checksum = htons(0);
if (mtu >= 0) {
if ((type != ICMPV6_PKT_TOOBIG) || (code != 0)) {
asprintf(error,
"ICMPv6 MTU is only valid for "
"packet_too_big-0");
return STATUS_ERR;
}
if (!is_valid_u32(mtu)) {
asprintf(error, "ICMPv6 MTU out of 32-bit range");
return STATUS_ERR;
}
icmpv6->message.packet_too_big.mtu = htonl(mtu);
}
return STATUS_OK;
}
/* Populate ICMP header fields. */
static int set_packet_icmp_header(struct packet *packet, void *icmp_header,
int address_family,
u8 type, u8 code, s64 mtu, char **error)
{
if (address_family == AF_INET) {
struct icmpv4 *icmpv4 = (struct icmpv4 *) icmp_header;
packet->icmpv4 = icmpv4;
assert(packet->icmpv6 == NULL);
return set_icmpv4_header(icmpv4, type, code, mtu, error);
} else if (address_family == AF_INET6) {
struct icmpv6 *icmpv6 = (struct icmpv6 *) icmp_header;
packet->icmpv6 = icmpv6;
assert(packet->icmpv4 == NULL);
return set_icmpv6_header(icmpv6, type, code, mtu, error);
} else {
assert(!"bad ip_version in config");
}
return STATUS_ERR;
}
/* Parse the given ICMP type and code strings, and fill in the
* *type and *code with the results. If there is an error during
* parsing, fill in *error and return STATUS_ERR; otherwise return
* STATUS_OK.
*/
static int parse_icmp_type_and_code(int address_family,
const char *type_string,
const char *code_string,
s32 *type, s32 *code, char **error)
{
int i = 0;
const struct icmp_type_info *icmp_types = NULL;
const struct icmp_code_info *code_table = NULL; /* for this type */
if (address_family == AF_INET)
icmp_types = icmpv4_types;
else if (address_family == AF_INET6)
icmp_types = icmpv6_types;
else
assert(!"bad ip_version in config");
/* Parse the type string. */
if (sscanf(type_string, "type_%d", type) == 1) {
/* Legal but non-standard type in tcpdump-inspired notation. */
} else {
/* Look in our table of known types. */
for (i = 0; icmp_types[i].type_string != NULL; ++i) {
if (!strcmp(type_string, icmp_types[i].type_string)) {
*type = icmp_types[i].type_byte;
code_table = icmp_types[i].code_table;
}
}
}
if (!is_valid_u8(*type)) {
asprintf(error, "bad ICMP type %s", type_string);
return STATUS_ERR;
}
/* Parse the code string. */
if (code_string == NULL) {
*code = 0; /* missing code means code = 0 */
} else if (sscanf(code_string, "code_%d", code) == 1) {
/* Legal but non-standard code in tcpdump-inspired notation. */
} else if (code_table != NULL) {
/* Look in our table of known codes. */
for (i = 0; code_table[i].code_string != NULL; ++i) {
if (!strcmp(code_string, code_table[i].code_string))
*code = code_table[i].code_byte;
}
}
if (!is_valid_u8(*code)) {
asprintf(error, "bad ICMP code %s", code_string);
return STATUS_ERR;
}
return STATUS_OK;
}
struct packet *new_icmp_packet(int address_family,
enum direction_t direction,
const char *type_string,
const char *code_string,
int protocol,
u32 tcp_start_sequence,
u32 payload_bytes,
s64 mtu,
char **error)
{
s32 type = -1; /* bad type; means "unknown so far" */
s32 code = -1; /* bad code; means "unknown so far" */
struct packet *packet = NULL; /* the newly-allocated result packet */
/* Calculate lengths in bytes of all sections of the packet.
* For now we only support the most common ICMP message
* format, which includes at the end the original outgoing IP
* header and the first 8 bytes after that (which will
* typically have the port info needed to demux the message).
*/
const int ip_fixed_bytes = ip_header_len(address_family);
const int ip_option_bytes = 0;
const int ip_header_bytes = ip_fixed_bytes + ip_option_bytes;
const int echoed_bytes = ip_fixed_bytes + ICMP_ECHO_BYTES;
const int icmp_bytes = icmp_header_len(address_family) + echoed_bytes;
const int ip_bytes = ip_header_bytes + icmp_bytes;
/* Sanity-check all the various lengths */
if (ip_option_bytes & 0x3) {
asprintf(error, "IP options are not padded correctly "
"to ensure IP header is a multiple of 4 bytes: "
"%d excess bytes", ip_option_bytes & 0x3);
goto error_out;
}
assert((ip_header_bytes & 0x3) == 0);
/* Parse the ICMP type and code */
if (parse_icmp_type_and_code(address_family, type_string, code_string,
&type, &code, error))
goto error_out;
assert(is_valid_u8(type));
assert(is_valid_u8(code));
/* Allocate and zero out a packet object of the desired size */
packet = packet_new(ip_bytes);
memset(packet->buffer, 0, ip_bytes);
packet->ip_bytes = ip_bytes;
packet->direction = direction;
packet->flags = 0;
packet->ecn = 0;
/* Set IP header fields */
const enum ip_ecn_t ecn = ECN_NONE;
set_packet_ip_header(packet, address_family, ip_bytes, direction, ecn,
icmp_protocol(address_family));
/* Find the start of the ICMP header and then populate common fields. */
void *icmp_header = packet_start(packet) + ip_header_bytes;
if (set_packet_icmp_header(packet, icmp_header, address_family,
type, code, mtu, error))
goto error_out;
/* All ICMP message types currently supported by this tool
* include a copy of the outbound IP header and the first few
* bytes inside. To ensure that the inbound ICMP message gets
* demuxed to the correct socket in the kernel, here we
* construct enough of a basic IP header and during test
* execution we fill in the port numbers and (if specified)
* TCP sequence number in the TCP header.
*/
u8 *echoed_ip = packet_echoed_ip_header(packet);
const int echoed_ip_bytes = (ip_fixed_bytes +
layer4_header_len(protocol) +
payload_bytes);
set_ip_header(echoed_ip, address_family, echoed_ip_bytes,
reverse_direction(direction), ecn, protocol);
if (protocol == IPPROTO_TCP) {
u32 *seq = packet_echoed_tcp_seq(packet);
*seq = htonl(tcp_start_sequence);
}
return packet;
error_out:
if (packet != NULL)
packet_free(packet);
return NULL;
}
/*
* Copyright 2013 Google Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/*
* Author: ncardwell@google.com (Neal Cardwell)
*
* Interface for module for formatting ICMP packets.
*/
#ifndef __ICMP_PACKET_H__
#define __ICMP_PACKET_H__
#include "types.h"
#include "packet.h"
/* Create and initialize a new struct packet containing an ICMP
* packet. The 'type_string' identifies the ICMP type. The
* 'code_string' identifies the ICMP code (and NULL means no code was
* provided, in which case we assume a default code of 0).
* The 'protocol' is either IPPROTO_UDP or IPPROTO_TCP.
* The 'tcp_start_sequence' and 'payload_bytes' describe the TCP or UDP
* packet echoed inside the ICMP message. The 'mtu' specifies the MTU
* advertised in "packet is too big" ICMP message, or -1 for no
* MTU. On success, returns a newly-allocated packet. On failure,
* returns NULL and fills in *error with an error message.
*/
extern struct packet *new_icmp_packet(int address_family,
enum direction_t direction,
const char *type_string,
const char *code_string,
int protocol,
u32 tcp_start_sequence,
u32 payload_bytes,
s64 mtu,
char **error);
#endif /* __ICMP_PACKET_H__ */
/*
* Copyright 2013 Google Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/*
* Author: ncardwell@google.com (Neal Cardwell)
*
* Our own ICMPv6 header declarations, so we have something that's
* portable and somewhat more readable than a typical system header
* file.
*/
#ifndef __ICMPV6_HEADERS_H__
#define __ICMPV6_HEADERS_H__
#include "types.h"
/* ICMPv6 hader. See RFC 4443. */
struct icmpv6 {
__u8 type;
__u8 code;
__sum16 checksum;
union {
struct {
__be32 unused;
} unreachable;
struct {
__be32 mtu;
} packet_too_big;
struct {
__be32 unused;
} time_exceeded;
struct {
__be32 pointer;
} parameter_problem;
} message;
};
/* Supported ICMPv6 types */
#define ICMPV6_DEST_UNREACH 1
#define ICMPV6_PKT_TOOBIG 2
#define ICMPV6_TIME_EXCEED 3
#define ICMPV6_PARAMPROB 4
/* Codes for ICMPV6 Destination Unreachable */
#define ICMPV6_NOROUTE 0
#define ICMPV6_ADM_PROHIBITED 1
#define ICMPV6_NOT_NEIGHBOUR 2
#define ICMPV6_ADDR_UNREACH 3
#define ICMPV6_PORT_UNREACH 4
/* Codes for ICMPV6 Time Exceeded */
#define ICMPV6_EXC_HOPLIMIT 0
#define ICMPV6_EXC_FRAGTIME 1
/* Codes for ICMPV6 Parameter Problem */
#define ICMPV6_HDR_FIELD 0
#define ICMPV6_UNK_NEXTHDR 1
#define ICMPV6_UNK_OPTION 2
#endif /* __ICMPV6_HEADERS_H__ */
/*
* Copyright 2013 Google Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/*
* Author: ncardwell@google.com (Neal Cardwell)
*
* Our own IPv4 header declarations, so we have something that's
* portable and somewhat more readable than a typical system header
* file.
*/
#ifndef __IP_HEADERS_H__
#define __IP_HEADERS_H__
#include "types.h"
struct ipv4 {
#if __BYTE_ORDER == __LITTLE_ENDIAN
__u8 ihl:4,
version:4;
#elif __BYTE_ORDER == __BIG_ENDIAN
__u8 version:4,
ihl:4;
#else
# error "Please fix endianness defines"
#endif
__u8 tos;
__be16 tot_len;
__be16 id;
__be16 frag_off;
__u8 ttl;
__u8 protocol;
__sum16 check;
struct in_addr src_ip;
struct in_addr dst_ip;
};
/* ----------------------- IP socket option values -------------------- */
/* Oddly enough, Linux distributions are typically missing even some
* of the older and more common IP socket options, such as IP_MTU.
*/
#ifdef linux
#define IP_TOS 1
#define IP_TTL 2
#define IP_HDRINCL 3
#define IP_OPTIONS 4
#define IP_ROUTER_ALERT 5
#define IP_RECVOPTS 6
#define IP_RETOPTS 7
#define IP_PKTINFO 8
#define IP_PKTOPTIONS 9
#define IP_MTU_DISCOVER 10
#define IP_RECVERR 11
#define IP_RECVTTL 12
#define IP_RECVTOS 13
#define IP_MTU 14
#define IP_FREEBIND 15
#define IP_IPSEC_POLICY 16
#define IP_XFRM_POLICY 17
#define IP_PASSSEC 18
#define IP_TRANSPARENT 19
#endif /* linux */
/* ECN: RFC 3168: http://tools.ietf.org/html/rfc3168 */
#define IP_ECN_MASK 3
#define IP_ECN_NONE 0
#define IP_ECN_ECT1 1
#define IP_ECN_ECT0 2
#define IP_ECN_CE 3
static inline u8 ipv4_ecn_bits(const struct ipv4 *ipv4)
{
return ipv4->tos & IP_ECN_MASK;
}
/* IP fragmentation bit flags */
#define IP_RF 0x8000 /* reserved fragment flag */
#define IP_DF 0x4000 /* don't fragment flag */
#define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1FFF /* mask for fragmenting bits */
#endif /* __IP_HEADERS_H__ */
/*
* Copyright 2013 Google Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/*
* Author: ncardwell@google.com (Neal Cardwell)
*
* Implementation for operations for IPv4 and IPv6 addresses.
*/
#include "ip_address.h"
#include <ifaddrs.h>
#include <net/if.h>
#include <stdlib.h>
#include <string.h>
#include "logging.h"
/* IPv6 prefix for IPv4-mapped addresses. These are in the
* ::FFFF:0:0/96 space, i.e. 10 bytes of 0x00 and 2 bytes of 0xFF. See
* RFC 4291 ("IPv6 Addressing Architecture") section 2.5.5.2
* ("IPv4-Mapped IPv6 Address").
*/
const u8 ipv4_mapped_prefix[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF };
int ip_address_length(int address_family)
{
switch (address_family) {
case AF_INET:
return sizeof(struct in_addr);
case AF_INET6:
return sizeof(struct in6_addr);
default:
die("ip_address_length: bad address family: %d\n",
address_family);
break;
}
return 0; /* not reached */
}
int sockaddr_length(int address_family)
{
switch (address_family) {
case AF_INET:
return sizeof(struct sockaddr_in);
case AF_INET6:
return sizeof(struct sockaddr_in6);
default:
die("sockaddr_length: bad address family: %d\n",
address_family);
break;
}
return 0; /* not reached */
}
static void ipv4_init(struct ip_address *ipv4)
{
memset(ipv4, 0, sizeof(*ipv4));
ipv4->address_family = AF_INET;
}
static void ipv6_init(struct ip_address *ipv6)
{
memset(ipv6, 0, sizeof(*ipv6));
ipv6->address_family = AF_INET6;
}
void ip_from_ipv4(const struct in_addr *ipv4, struct ip_address *ip)
{
ipv4_init(ip);
ip->ip.v4 = *ipv4;
}
void ip_from_ipv6(const struct in6_addr *ipv6, struct ip_address *ip)
{
ipv6_init(ip);
ip->ip.v6 = *ipv6;
}
void ip_to_ipv4(const struct ip_address *ip, struct in_addr *ipv4)
{
*ipv4 = ip->ip.v4;
}
void ip_to_ipv6(const struct ip_address *ip, struct in6_addr *ipv6)
{
*ipv6 = ip->ip.v6;
}
struct ip_address ipv4_parse(const char *ip_string)
{
struct ip_address ipv4;
ipv4_init(&ipv4);
if (inet_pton(AF_INET, ip_string, &ipv4.ip.v4) != 1)
die("bad IPv4 address: %s\n", ip_string);
return ipv4;
}
struct ip_address ipv6_parse(const char *ip_string)
{
struct ip_address ipv6;
ipv6_init(&ipv6);
if (inet_pton(AF_INET6, ip_string, &ipv6.ip.v6) != 1)
die("bad IPv6 address: %s\n", ip_string);
return ipv6;
}
const char *ip_to_string(const struct ip_address *ip, char *buffer)
{
if (!inet_ntop(ip->address_family, &ip->ip, buffer, ADDR_STR_LEN))
die_perror("inet_ntop");
return buffer;
}
struct ip_address ipv6_map_from_ipv4(const struct ip_address ipv4)
{
struct ip_address ipv6;
ipv6_init(&ipv6);
assert(sizeof(ipv4.ip.v4) + sizeof(ipv4_mapped_prefix) ==
sizeof(ipv6.ip.v6));
memcpy(ipv6.ip.v6.s6_addr, ipv4_mapped_prefix,
sizeof(ipv4_mapped_prefix));
memcpy(ipv6.ip.v6.s6_addr + sizeof(ipv4_mapped_prefix),
&ipv4.ip.v4, sizeof(ipv4.ip.v4));
return ipv6;
}
int ipv6_map_to_ipv4(const struct ip_address ipv6, struct ip_address *ipv4)
{
if (memcmp(&ipv6.ip.v6.s6_addr,
ipv4_mapped_prefix, sizeof(ipv4_mapped_prefix)) == 0) {
ipv4_init(ipv4);
memcpy(&ipv4->ip.v4,
ipv6.ip.v6.s6_addr + sizeof(ipv4_mapped_prefix),
sizeof(ipv4->ip.v4));
return STATUS_OK;
} else {
return STATUS_ERR;
}
}
/* Fill in a sockaddr struct and socklen_t using the given IPv4
* address and port.
*/
static void ipv4_to_sockaddr(const struct ip_address *ipv4, u16 port,
struct sockaddr *address, socklen_t *length)
{
struct sockaddr_in sa_v4;
memset(&sa_v4, 0, sizeof(sa_v4));
#ifndef linux
sa_v4.sin_len = sizeof(sa_v4);
#endif
sa_v4.sin_family = AF_INET;
sa_v4.sin_port = htons(port);
memcpy(&sa_v4.sin_addr, &ipv4->ip.v4, sizeof(sa_v4.sin_addr));
*length = sizeof(sa_v4);
memcpy(address, &sa_v4, *length);
}
/* Fill in a sockaddr struct and socklen_t using the given IPv6
* address and port.
*/
static void ipv6_to_sockaddr(const struct ip_address *ipv6, u16 port,
struct sockaddr *address, socklen_t *length)
{
struct sockaddr_in6 sa_v6;
memset(&sa_v6, 0, sizeof(sa_v6));
#ifndef linux
sa_v6.sin6_len = sizeof(sa_v6);
#endif
sa_v6.sin6_family = AF_INET6;
sa_v6.sin6_port = htons(port);
memcpy(&sa_v6.sin6_addr, &ipv6->ip.v6, sizeof(sa_v6.sin6_addr));
*length = sizeof(sa_v6);
memcpy(address, &sa_v6, *length);
}
void ip_to_sockaddr(const struct ip_address *ip, u16 port,
struct sockaddr *address, socklen_t *length)
{
switch (ip->address_family) {
case AF_INET:
ipv4_to_sockaddr(ip, port, address, length);
break;
case AF_INET6:
ipv6_to_sockaddr(ip, port, address, length);
break;
default:
die("ip_to_sockaddr: bad address family: %d\n",
ip->address_family);
break;
}
}
/* Extract and return the IPv4 address and port from the given sockaddr. */
static void ipv4_from_sockaddr(const struct sockaddr *address, socklen_t length,
struct ip_address *ipv4, u16 *port)
{
assert(address->sa_family == AF_INET);
ipv4_init(ipv4);
struct sockaddr_in sa_v4;
assert(length == sizeof(sa_v4));
memcpy(&sa_v4, address, length); /* to avoid aliasing issues */
ipv4->ip.v4 = sa_v4.sin_addr;
*port = ntohs(sa_v4.sin_port);
}
/* Extract and return the IPv6 address and port from the given sockaddr. */
static void ipv6_from_sockaddr(const struct sockaddr *address, socklen_t length,
struct ip_address *ipv4, u16 *port)
{
assert(address->sa_family == AF_INET6);
ipv6_init(ipv4);
struct sockaddr_in6 sa_v6;
assert(length == sizeof(sa_v6));
memcpy(&sa_v6, address, length); /* to avoid aliasing issues */
ipv4->ip.v6 = sa_v6.sin6_addr;
*port = ntohs(sa_v6.sin6_port);
}
void ip_from_sockaddr(const struct sockaddr *address, socklen_t length,
struct ip_address *ip, u16 *port)
{
switch (address->sa_family) {
case AF_INET:
ipv4_from_sockaddr(address, length, ip, port);
break;
case AF_INET6:
ipv6_from_sockaddr(address, length, ip, port);
break;
default:
die("ip_from_sockaddr: bad address family: %d\n",
address->sa_family);
break;
}
}
int get_ip_device(const struct ip_address *ip, char *dev_name)
{
struct ifaddrs *ifaddr_list, *ifaddr;
bool is_local = false;
if (getifaddrs(&ifaddr_list))
die_perror("getifaddrs");
for (ifaddr = ifaddr_list; ifaddr != NULL; ifaddr = ifaddr->ifa_next) {
int family;
struct ip_address interface_ip;
u16 port;
if (ifaddr->ifa_addr == NULL)
continue;
family = ifaddr->ifa_addr->sa_family;
if (family != ip->address_family)
continue;
ip_from_sockaddr(ifaddr->ifa_addr, sockaddr_length(family),
&interface_ip, &port);
if (is_equal_ip(ip, &interface_ip)) {
assert(ifaddr->ifa_name);
assert(strlen(ifaddr->ifa_name) < IFNAMSIZ);
strcpy(dev_name, ifaddr->ifa_name);
is_local = true;
break;
}
}
freeifaddrs(ifaddr_list);
return is_local;
}
int is_ip_local(const struct ip_address *ip)
{
char dev_name[IFNAMSIZ];
return get_ip_device(ip, dev_name);
}
extern int netmask_to_prefix(const char *netmask)
{
int pos;
struct ip_address mask = ipv4_parse(netmask);
u32 mask_addr = ntohl(mask.ip.v4.s_addr);
int prefix_len = 0;
for (pos = 31; pos >= 0; --pos) {
if (!(mask_addr & (1<<pos)))
break;
++prefix_len;
}
return prefix_len;
}
/*
* Copyright 2013 Google Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/*
* Author: ncardwell@google.com (Neal Cardwell)
*
* Types and operations for IPv4 and IPv6 addresses.
*/
#ifndef __IP_ADDRESS_H__
#define __IP_ADDRESS_H__
#include "types.h"
#include <netinet/in.h>
/* IPv4 or IPv6 address. */
struct ip_address {
int address_family; /* AF_INET or AF_INET6 */
union {
struct in_addr v4;
struct in6_addr v6;
} ip; /* IP address (network order) */
};
static inline void ip_reset(struct ip_address *ip)
{
memset(ip, 0, sizeof(*ip));
}
/* Fill in an ip_address using the given family-specific struct. */
extern void ip_from_ipv4(const struct in_addr *ipv4, struct ip_address *ip);
extern void ip_from_ipv6(const struct in6_addr *ipv6, struct ip_address *ip);
/* Fill in the given family-specific struct using the given ip_address. */
extern void ip_to_ipv4(const struct ip_address *ip, struct in_addr *ipv4);
extern void ip_to_ipv6(const struct ip_address *ip, struct in6_addr *ipv6);
/* Return the number of bytes in the on-the-wire representation of
* addresses of the given family.
*/
extern int ip_address_length(int address_family);
/* Return the number of bytes in sockaddr of the given family. */
extern int sockaddr_length(int address_family);
/* Return true iff the two addresses are the same. */
static inline bool is_equal_ip(const struct ip_address *a,
const struct ip_address *b)
{
return ((a->address_family == b->address_family) &&
!memcmp(&a->ip, &b->ip, ip_address_length(a->address_family)));
}
/* Parse a human-readable IPv4 address and return it. Print an error
* to stderr and exit if there is an error parsing the address.
*/
extern struct ip_address ipv4_parse(const char *ip_string);
/* Parse a human-readable IPv6 address and return it. Print an error
* to stderr and exit if there is an error parsing the address.
*/
extern struct ip_address ipv6_parse(const char *ip_string);
/* Print a human-readable representation of the given IP address in the
* given buffer, which must be at least ADDR_STR_LEN bytes long.
* Returns a pointer to the given buffer.
*/
extern const char *ip_to_string(const struct ip_address *ip, char *buffer);
/* Create an IPv4-mapped IPv6 address. */
extern struct ip_address ipv6_map_from_ipv4(const struct ip_address ipv4);
/* Deconstruct an IPv4-mapped IPv6 address and fill in *ipv4 with the
* IPv4 address that was mapped into IPv6 space. Return STATUS_OK on
* success, or STATUS_ERR on failure (meaning the input ipv6 was not
* actually an IPv4-mapped IPv6 address).
*/
extern int ipv6_map_to_ipv4(const struct ip_address ipv6,
struct ip_address *ipv4);
/* Fill in a sockaddr struct and socklen_t using the given IP and port.
* The IP address may be IPv4 or IPv6.
*/
extern void ip_to_sockaddr(const struct ip_address *ip, u16 port,
struct sockaddr *address, socklen_t *length);
/* Fill in an IP address and port by parsing a sockaddr struct and
* socklen_t using the given IP and port. The IP address may be IPv4
* or IPv6. Exits with an error message if the address family is other
* than AF_INET or AF_INET6.
*/
extern void ip_from_sockaddr(const struct sockaddr *address, socklen_t length,
struct ip_address *ip, u16 *port);
/* Return true iff the address is that of a local interface. */
/* Note: this should return bool, but that doesn't compile on NetBSD. */
extern int is_ip_local(const struct ip_address *ip);
/* Fill in the name of the device configured with the given IP, if
* any. The dev_name buffer should be at least IFNAMSIZ bytes.
* Return true iff the IP is found on a local device.
*/
/* Note: this should return bool, but that doesn't compile on NetBSD. */
extern int get_ip_device(const struct ip_address *ip, char *dev_name);
/* Convert dotted decimal netmask to equivalent CIDR prefix length */
extern int netmask_to_prefix(const char *netmask);
#endif /* __IP_ADDRESS_H__ */
/*
* Copyright 2013 Google Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/*
* Author: ncardwell@google.com (Neal Cardwell)
*
* Implementation for module for formatting IPv4 and IPv6 packets.
*/
#include "ip_packet.h"
#include "ip.h"
#include "ipv6.h"
/* Return the ECN header bits for the given ECN treatment. */
static u8 ip_ecn_bits(enum ip_ecn_t ecn)
{
if (ecn == ECN_ECT0)
return IP_ECN_ECT0;
else if (ecn == ECN_ECT1)
return IP_ECN_ECT1;
else if (ecn == ECN_CE)
return IP_ECN_CE;
else
return 0;
}
/* Fill in IPv4 header fields. */
static void set_ipv4_header(struct ipv4 *ipv4,
u16 ip_bytes,
enum direction_t direction,
enum ip_ecn_t ecn, u8 protocol)
{
ipv4->version = 4;
ipv4->ihl = sizeof(struct ipv4) / sizeof(u32);
ipv4->tos = ip_ecn_bits(ecn);
ipv4->tot_len = htons(ip_bytes);
ipv4->id = 0;
ipv4->frag_off = 0;
ipv4->ttl = 255;
ipv4->protocol = protocol;
ipv4->check = 0;
ipv4->src_ip = in4addr_any;
ipv4->dst_ip = in4addr_any;
}
/* Fill in IPv6 header fields. */
static void set_ipv6_header(struct ipv6 *ipv6,
u16 ip_bytes,
enum direction_t direction,
enum ip_ecn_t ecn, u8 protocol)
{
ipv6->version = 6;
ipv6->traffic_class_hi = 0;
ipv6->traffic_class_lo = ip_ecn_bits(ecn);
ipv6->flow_label_hi = 0;
ipv6->flow_label_lo = 0;
assert(ip_bytes >= sizeof(*ipv6));
ipv6->payload_len = htons(ip_bytes - sizeof(*ipv6));
ipv6->next_header = protocol;
ipv6->hop_limit = 255;
ipv6->src_ip = in6addr_any;
ipv6->dst_ip = in6addr_any;
}
void set_ip_header(void *ip_header,
int address_family,
u16 ip_bytes,
enum direction_t direction,
enum ip_ecn_t ecn, u8 protocol)
{
if (address_family == AF_INET)
set_ipv4_header(ip_header, ip_bytes, direction, ecn, protocol);
else if (address_family == AF_INET6)
set_ipv6_header(ip_header, ip_bytes, direction, ecn, protocol);
else
assert(!"bad ip_version in config");
}
void set_packet_ip_header(struct packet *packet,
int address_family,
u16 ip_bytes,
enum direction_t direction,
enum ip_ecn_t ecn, u8 protocol)
{
if (address_family == AF_INET) {
struct ipv4 *ipv4 = (struct ipv4 *) packet->buffer;
packet->ipv4 = ipv4;
assert(packet->ipv6 == NULL);
set_ipv4_header(ipv4, ip_bytes, direction, ecn, protocol);
} else if (address_family == AF_INET6) {
struct ipv6 *ipv6 = (struct ipv6 *) packet->buffer;
packet->ipv6 = ipv6;
assert(packet->ipv4 == NULL);
set_ipv6_header(ipv6, ip_bytes, direction, ecn, protocol);
} else {
assert(!"bad ip_version in config");
}
}
/*
* Copyright 2013 Google Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/*
* Author: ncardwell@google.com (Neal Cardwell)
*
* Interface for module for formatting IP packets.
*/
#ifndef __IP_PACKET_H__
#define __IP_PACKET_H__
#include "types.h"
#include "packet.h"
/* Populate header fields in the IP header at the given address. */
extern void set_ip_header(void *ip_header,
int address_family,
u16 ip_bytes,
enum direction_t direction,
enum ip_ecn_t ecn, u8 protocol);
/* Set the packet's IP header pointer and then populate the IP header fields. */
extern void set_packet_ip_header(struct packet *packet,
int address_family,
u16 ip_bytes,
enum direction_t direction,
enum ip_ecn_t ecn, u8 protocol);
#endif /* __IP_PACKET_H__ */
/*
* Copyright 2013 Google Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/*
* Author: ncardwell@google.com (Neal Cardwell)
*
* Our own IPv6 header declarations, so we have something that's
* portable and somewhat more readable than a typical system header
* file.
*/
#ifndef __IPV6_HEADERS_H__
#define __IPV6_HEADERS_H__
#include "types.h"
#include <netinet/in.h>
struct ipv6 {
#if __BYTE_ORDER == __LITTLE_ENDIAN
__u8 traffic_class_hi:4,
version:4;
__u8 flow_label_hi:4,
traffic_class_lo:4;
__u16 flow_label_lo;
#elif __BYTE_ORDER == __BIG_ENDIAN
__u8 version:4,
traffic_class_hi:4;
__u8 traffic_class_lo:4,
flow_label_hi:4;
__u16 flow_label_lo;
#else
# error "Please fix endianness defines"
#endif
__be16 payload_len;
__u8 next_header;
__u8 hop_limit;
struct in6_addr src_ip;
struct in6_addr dst_ip;
};
/* ECN: RFC 3168: http://tools.ietf.org/html/rfc3168 */
static inline u8 ipv6_ecn_bits(const struct ipv6 *ipv6)
{
return ipv6->traffic_class_lo & IP_ECN_MASK;
}
/* The following struct declaration is needed for the IPv6 ioctls
* SIOCSIFADDR and SIOCDIFADDR that add and delete IPv6 addresses from
* a network interface. We have to declare our own version here
* because this struct is only available in /usr/include/linux/ipv6.h,
* but that .h file has kernel IPv6 declarations that conflict with
* standard user-space IPv6 declarations.
*/
struct in6_ifreq {
struct in6_addr ifr6_addr;
__u32 ifr6_prefixlen;
int ifr6_ifindex;
};
#endif /* __IPV6_HEADERS_H__ */
%{
/*
* Copyright 2013 Google Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/*
* Author: ncardwell@google.com (Neal Cardwell)
*
* This is the specification for the lexical scanner for the packetdrill
* script language. It is processed by the flex lexical scanner
* generator.
*
* For full documentation see: http://flex.sourceforge.net/manual/
*
* Here is a quick and dirty tutorial on flex:
*
* A flex lexical scanner specification is basically a list of rules,
* where each rule is a regular expressions for a lexical token to
* match, followed by a C fragment to execute when the scanner sees
* that pattern.
*
* The lexer feeds a stream of terminal symbols up to this parser,
* passing up a FOO token for each "return FOO" in the lexer spec. The
* lexer specifies what value to pass up to the parser by setting a
* yylval.fooval field, where fooval is a field in the %union in the
* .y file.
*
* TODO: detect overflow in numeric literals.
*/
#include "types.h"
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include "script.h"
#include "tcp_options.h"
/* This include of the bison-generated .h file must go last so that we
* can first include all of the declarations on which it depends.
*/
#include "parser.h"
/* Suppress flex's generation of an uncalled static input() function, which
* leads to a compiler warning:
* warning: ‘input’ defined but not used
*/
#define YY_NO_INPUT
/* Copy the string name "foo" after the "--" of a "--foo" option. */
static char *option(const char *s)
{
const int dash_dash_len = 2;
return strndup(s + dash_dash_len, strlen(s) - dash_dash_len);
}
/* Copy the string inside a quoted string. */
static char *quoted(const char *s)
{
const int delim_len = 1;
return strndup(s + delim_len, strlen(s) - 2*delim_len);
}
/* Copy the code inside a code snippet that is enclosed in %{ }% after
* first stripping the space and tab characters from either end of the
* snippet. We strip leading and trailing whitespace for Python users
* to remain sane, since Python is sensitive to whitespace. To summarize,
* given an input %{<space><code><space>}% we return: <code>
*/
static char *code(const char *s)
{
const int delim_len = sizeof("%{")-1;
const char *start = s + delim_len;
while ((*start == ' ') || (*start == '\t'))
++start;
const char *end = s + (strlen(s) - 1) - delim_len;
while ((*end == ' ') || (*end == '\t'))
--end;
const int code_len = end - start + 1;
return strndup(start, code_len);
}
/* Convert a hex string prefixed by "0x" to an integer value. */
static s64 hextol(const char *s)
{
return strtol(yytext + 2, NULL, 16);
}
%}
%{
#define YY_USER_ACTION yylloc.first_line = yylloc.last_line = yylineno;
%}
%option yylineno
%option nounput
/* A regexp for C++ comments: */
cpp_comment \/\/[^\n]*\n
/* Here is a summary of the regexp for C comments:
* open-comment
* any number of:
* (non-stars) or (star then non-slash)
* close comment
*/
c_comment \/\*(([^*])|(\*[^\/]))*\*\/
/* The regexp for code snippets is analogous to that for C comments.
* Here is a summary of the regexp for code snippets:
* %{
* any number of:
* (non-}) or (} then non-%)
* }%
*/
code \%\{(([^}])|(\}[^\%]))*\}\%
/* A regular experssion for an IP address
* TODO(ncardwell): IPv6
*/
ip_addr [0-9]+[.][0-9]+[.][0-9]+[.][0-9]+
%%
sa_family return SA_FAMILY;
sin_port return SIN_PORT;
sin_addr return SIN_ADDR;
msg_name return MSG_NAME;
msg_iov return MSG_IOV;
msg_flags return MSG_FLAGS;
fd return FD;
events return EVENTS;
revents return REVENTS;
onoff return ONOFF;
linger return LINGER;
htons return _HTONS_;
icmp return ICMP;
udp return UDP;
inet_addr return INET_ADDR;
ack return ACK;
eol return EOL;
ecr return ECR;
mss return MSS;
mtu return MTU;
nop return NOP;
sack return SACK;
sackOK return SACKOK;
TS return TIMESTAMP;
FO return FAST_OPEN;
val return VAL;
win return WIN;
wscale return WSCALE;
ect01 return ECT01;
ect0 return ECT0;
ect1 return ECT1;
noecn return NO_ECN;
ce return CE;
[.][.][.] return ELLIPSIS;
--[a-zA-Z0-9_]+ yylval.string = option(yytext); return OPTION;
[-]?[0-9]*[.][0-9]+ yylval.floating = atof(yytext); return FLOAT;
[-]?[0-9]+ yylval.integer = atoll(yytext); return INTEGER;
0x[0-9]+ yylval.integer = hextol(yytext); return HEX_INTEGER;
[a-zA-Z0-9_]+ yylval.string = strdup(yytext); return WORD;
\"(\\.|[^"])*\" yylval.string = quoted(yytext); return STRING;
\`(\\.|[^`])*\` yylval.string = quoted(yytext); return BACK_QUOTED;
[^ \t\n] return (int) yytext[0];
[ \t\n]+ /* ignore whitespace */;
{cpp_comment} /* ignore C++-style comment */;
{c_comment} /* ignore C-style comment */;
{code} yylval.string = code(yytext); return CODE;
{ip_addr} yylval.string = strdup(yytext); return IP_ADDR;
%%
/*
* Copyright 2013 Google Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/*
* Author: ncardwell@google.com (Neal Cardwell)
*
* Link-layer utilities.
*/
#include "link_layer.h"
#include <stdlib.h>
#include <unistd.h>
#include "logging.h"
#ifdef linux
#include <net/if.h>
#include <sys/ioctl.h>
void get_hw_address(const char *name, struct ether_addr *hw_address)
{
u8 *m = NULL;
struct ifreq ifr;
int fd;
DEBUGP("get_hw_address for device %s\n", name);
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (fd < 0)
die_perror("socket(AF_INET, SOCK_DGRAM, IPPROTO_IP)");
/* Discover the index of the interface. */
snprintf(ifr.ifr_name, IFNAMSIZ, "%s", name);
if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0)
die_perror("ioctl SIOCGIFINDEX");
/* Get hardware address for the interface. */
if (ioctl(fd, SIOCGIFHWADDR, &ifr) != 0)
die_perror("ioctl SIOCGIFHWADDR");
m = (u8 *)&ifr.ifr_addr.sa_data;
DEBUGP("%s HWaddr: %02x:%02x:%02x:%02x:%02x:%02x\n",
name, m[0], m[1], m[2], m[3], m[4], m[5]);
memcpy(hw_address, m, sizeof(*hw_address));
if (close(fd))
die_perror("close");
}
#else
#if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__)
#include <net/if_types.h>
#include <net/if_dl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#endif /* defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) */
void get_hw_address(const char *name, struct ether_addr *hw_address)
{
struct ifaddrs *ifaddrs_list, *ifaddr;
DEBUGP("get_hw_address for device %s\n", name);
if (getifaddrs(&ifaddrs_list) < 0)
die_perror("getifaddrs");
for (ifaddr = ifaddrs_list; ifaddr != NULL; ifaddr = ifaddr->ifa_next) {
if (strcmp(name, ifaddr->ifa_name) == 0 &&
ifaddr->ifa_addr->sa_family == AF_LINK) {
struct sockaddr_dl *sdl;
sdl = (struct sockaddr_dl *)ifaddr->ifa_addr;
if (sdl->sdl_type == IFT_ETHER) {
memcpy(hw_address, LLADDR(sdl),
sizeof(*hw_address));
freeifaddrs(ifaddrs_list);
return;
}
}
}
die("unable to find hw address for %s\n", name);
}
#endif
/*
* Copyright 2013 Google Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/*
* Author: ncardwell@google.com (Neal Cardwell)
*
* Link-layer utilities.
*/
#ifndef __LINK_LAYER_H__
#define __LINK_LAYER_H__
#include "types.h"
#include "ethernet.h"
/* Get the link layer address for the device with the given name, or die. */
void get_hw_address(const char *name, struct ether_addr *hw_address);
#endif /* __LINK_LAYER_H__ */
/*
* Copyright 2013 Google Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/*
* Author: ncardwell@google.com (Neal Cardwell)
*
* Logging and output functions.
*/
#include "logging.h"
#include <stdarg.h>
#include <stdlib.h>
extern void die(char *format, ...)
{
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
exit(EXIT_FAILURE);
}
void die_perror(char *message)
{
perror(message);
exit(EXIT_FAILURE);
}
/*
* Copyright 2013 Google Inc.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/*
* Author: ncardwell@google.com (Neal Cardwell)
*
* Logging and output functions.
*/
#ifndef __LOGGING_H__
#define __LOGGING_H__
#include "types.h"
/* Enable this to get debug logging. */
#define DEBUG_LOGGING 0
/* Use a gcc variadic macro to conditionally compile debug printing. */
#define DEBUGP(...) \
if (DEBUG_LOGGING) { \
fprintf(stdout, __VA_ARGS__); \
fflush(stdout); \
}
/* Log the message to stderr and then exit with a failure status code. */
extern void die(char *format, ...);
/* Call perror() with message and then exit with a failure status code. */
extern void die_perror(char *message);
#endif /* __LOGGING_H__ */
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment