Skip to content

Instantly share code, notes, and snippets.

@goldsborough
Created June 14, 2016 20:04
Show Gist options
  • Save goldsborough/275bacff18fbcd373a52a7fad07e3e77 to your computer and use it in GitHub Desktop.
Save goldsborough/275bacff18fbcd373a52a7fad07e3e77 to your computer and use it in GitHub Desktop.
Working IPv6 Neighbor Discovery
#include <arpa/inet.h>
#include <asm/byteorder.h>
#include <assert.h>
#include <errno.h>
#include <net/ethernet.h>
#include <netinet/ether.h>
#include <netinet/icmp6.h>
#include <netinet/ip6.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <ctype.h>
#include "arguments.h"
#include "checksums.h"
#include "hexdump.h"
#include "raw.h"
#define ICMPv6_NEXT_HEADER 0x3A
#define IPv6_ETHER_TYPE htons(0x86DD)
#define IPv6_VERSION 0x06
#define ICMPv6_ND_CODE 0x00
#define ICMPv6_ND_SOLICIT_TYPE ND_NEIGHBOR_SOLICIT
#define ICMPv6_ND_ADVERT_TYPE ND_NEIGHBOR_ADVERT
#define ND_OPTION_LENGTH 0x01
#define ND_HOP_LIMIT 0xFF
#define IPv6_ADDR_LEN 16
#define RECEIVE_BUFFER_LENGTH 1514
#define IPv6_ADDR_LENGTH sizeof(struct in6_addr)
#define MIN_PACKET_LENGTH sizeof(struct icmp6_neighbor_solicit)
#define MIN_ICMPv6_PAYLOAD_LENGTH sizeof(struct neighbor_solicit_payload)
#define MIN_IPv6_TOTAL_LENGTH \
sizeof(struct ipv6_hdr) + sizeof(struct neighbor_solicit_payload)
#define ROUTER_FLAG 0x80
#define SOLICITED_FLAG 0x40
#define OVERRIDE_FLAG 0x20
typedef enum { false, true } bool;
typedef uint8_t *ether_addr;
#define FAILURE false
#define SUCCESS true
/* Extracted from /usr/include/linux/if_ethernet.h (just for reference):
* #define ETH_ALEN 6 Octets in one ethernet addr
*
* Extracted from /usr/include/net/ethernet.h (just for reference):
* struct ether_header
* {
* u_int8_t ether_dhost[ETH_ALEN]; destination eth addr
* u_int8_t ether_shost[ETH_ALEN]; source ether addr
* u_int16_t ether_type; packet type ID field
* }
*/
/* Extracted from /usr/include/netinet/in.h (just for reference):
* struct in6_addr
* {
* union
* {
* uint8_t __u6_addr8[16];
* #ifdef __USE_MISC
* uint16_t __u6_addr16[8];
* uint32_t __u6_addr32[4];
* #endif
* } __in6_u;
* #define s6_addr __in6_u.__u6_addr8
* #ifdef __USE_MISC
* # define s6_addr16 __in6_u.__u6_addr16
* # define s6_addr32 __in6_u.__u6_addr32
* #endif
* };
*
* "hdr->s6_addr" will work as access.
*/
/*
* We do not use the kernel's definition of the IPv6 header (struct ipv6hdr)
* because the definition there is slightly different from what we would
* expect
* (the problem is the 20bit flow label - 20bit is brain-damaged).
*
* Instead, we provide you struct that directly maps to the RFCs and lecture
* slides below.
*/
struct ipv6_hdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
uint32_t tc1 : 4, version : 4, flow_label1 : 4, tc2 : 4, flow_label2 : 16;
#elif defined(__BIG_ENDIAN_BITFIELD)
uint32_t version : 4, tc1 : 4, tc2 : 4, flow_label1 : 4, flow_label2 : 16;
#else
#error "You did something wrong"
#endif
uint16_t plen;
uint8_t nxt;
uint8_t hlim;
struct in6_addr src;
struct in6_addr dst;
} __attribute__((packed));
/*====================================TODO===================================*/
/* First, declare struct useful for package dissection, i.e., a struct
* containing pointers to the various headers and header fields that will be
* set by your dissector (the function that tests whether or not a received
* frame/packet is valid).
*/
/*
* It is very useful to look into the files at /usr/include/netinet for
* existing structs that may help to create these. Also have a look at
* assignment2.c, e.g. 'struct wol'.
*/
struct icmp6_header {
uint8_t type;
uint8_t code;
uint16_t checksum;
} __attribute__((packed));;
struct icmp_option_header {
uint8_t type;
uint8_t length;
} __attribute__((packed));;
struct nd_option {
struct icmp_option_header header;
uint8_t link_address[ETH_ALEN];
} __attribute__((packed));;
/* This struct should contain the icmp part of your message (ip-payload) */
struct neighbor_solicit_payload {
struct icmp6_header icmp6;
uint32_t reserved;
struct in6_addr target_address;
struct nd_option option;
} __attribute__((packed));
/* This struct should reserve enough space for the complete packet */
struct icmp6_neighbor_solicit {
struct ether_header ether;
struct ipv6_hdr ip;
struct neighbor_solicit_payload payload;
} __attribute__((packed));
/* This struct should contain the icmp part of the advertisement (ip-payload) */
struct neighbor_advertise_payload {
struct icmp6_header icmp6;
uint8_t flags;
uint8_t reserved [3];
struct in6_addr target_address;
uint8_t options [];
} __attribute__((packed));
/* This struct should reserve enough space for the complete packet */
struct icmp6_neighbor_advertise {
struct ether_header ether;
struct ipv6_hdr ip;
struct neighbor_advertise_payload payload;
} __attribute__((packed));
/**
* Checks a flag in the ICMPv6 payload's flag set.
*
* @param payload The NDP advertisement payload.
* @param flag The flag to check.
*
* @return The boolean value of the flag.
*
*/
bool is_set(const struct neighbor_advertise_payload* payload, int flag) {
return payload->flags & flag;
}
/**
* Determines the number of neighbor discovery options in an IMCPv6 Payload.
*
* @param packet The neighbor advertisement packet.
*
* @return The number of NDP options.
*
*/
int get_nd_option_bytes(struct icmp6_neighbor_advertise* packet) {
// Note that the sizeof operator evaluated on a struct with a flexible
// array member (empty []) gives the size of the struct *up to the array*
// i.e. the offset of the array pointer. This is precisely what we want here,
// since plen is the whole size of the payload.
return ntohs(packet->ip.plen) - sizeof(struct neighbor_advertise_payload);
}
/**
* Tests whether a character is one of the allowed
* hexadecimal alphabetic characters ([a-f]).
*
* @param character The character to check.
*
* @return true if the character is in [a-f], else false.
*/
bool ishexalpha(char character) {
return character >= 'a' && character <= 'z';
}
/**
* Checks if a character is part of the hexadecimal alphabet ([0-9a-f]).
*
* @param character The character to check.
*
* @return true if the character is hexadecimal, else false.
*/
bool ishex(char character) {
return isdigit(character) || ishexalpha(character);
}
/**
* Lowers both characters of an octet.
*
* @param A pointer to an array of two characters to lower.
*/
void make_octet_lower(char *chars) {
chars[0] = tolower(chars[0]);
chars[1] = tolower(chars[1]);
}
/**
* Parses a hexadecimal ASCII digit into the
* corresponding hexadecimal number.
*
* E.g. 'a' => 10, '5' => 5
*
* @param character The ASCII character to parse.
*
* @return The numeric value of the hexadecimal value
* represented by the character.
*/
uint8_t parse_hex_digit(char character) {
if (isdigit(character)) {
return character - '0';
} else {
return character - 'a' + 10;
}
}
/**
* Parses the two hex digits (as ASCII characters) pointed
* to by octet into a singe byte value.
*
* @param octet The two hex digits to conver to a byte.
*
* @return The corresponding byte.
*/
int parse_octet(char *octet) {
uint8_t result;
make_octet_lower(octet);
if (!ishex(octet[0]) || !ishex(octet[1])) return -1;
// Grab lower nibble
result = parse_hex_digit(octet[1]);
// Grab upper nibble
result |= parse_hex_digit(octet[0]) << 4;
return result;
}
/*
* Counts the number of empty groups in an IPv6 address.
* E.g. in a::b the number of empty groups would be six.
*
* @param ip The IP address to count the number of empty groups in.
*
* @return The number of empty groups in the IP address.
*/
int count_groups(const char* ip) {
int count = 0;
bool has_empty_groups = false;
for (; *ip != '\0'; ++ip) {
if(*ip == ':') {
if (*(ip + 1) == ':') {
// Should only come here once
if (has_empty_groups) return -1;
has_empty_groups = true;
}
++count;
}
}
// If we didn't count any empty groups, we expect 7
if (!has_empty_groups && count != 7) return -1;
// For 8 groups we can have at most 7 delimiters
if (count > 7) return -1;
return count;
}
/*
* This function parses the ip address from a string into an network byte order
* representation
*
* @param dst_ip A pointer to a struct in6_addr where the ip will be written to
* @param ipaddr The ip address as null terminated string
*
* @return 1 on success
* 0 on error (NOTE: I changed this to have a uniform FAILURE code)
*/
bool parse_ip(const char *original_ip_addr, struct in6_addr *dst_ip) {
size_t index = 0;
char ip[INET6_ADDRSTRLEN];
char* token = ip;
char* colon;
int number_of_groups;
if (original_ip_addr == NULL || dst_ip == NULL) return -1;
// strok will modify our IP address
strcpy(ip, original_ip_addr);
// Set all to zero first so we only have
// to fill in the non-empty groups
memset(dst_ip->s6_addr, 0, IPv6_ADDR_LEN);
if ((number_of_groups = count_groups(original_ip_addr)) == -1) {
fprintf(stderr, "'%s' is not a valid IPv6 address!\n", original_ip_addr);
}
for (colon = strchr(ip, ':'); true; colon = strchr(colon, ':')) {
char group[4];
int octet;
int length;
// If we just found an empty group and are not at the start
// then this is the empty group in the middle, i.e. the ::
if (token == colon && token != ip) {
// Skip all the empty groups (note we set everything to zero
// first so this is OK). It even deals well with the invalid
// case of compressing a single 0 field, i.e. a:b:c::e:f:g:h,
// which is actually invalid. That group would simply be skipped.
index += (7 - number_of_groups + 1) * 2;
} else {
if (colon) *colon = '\0';
// Pad with zero (characters!) first
memset(group, '0', 4);
// Copy the rest of the group into the upper chars of
// the group string (so that lower zeros are taken into account)
// E.g. ff => 00ff
length = strlen(token);
strncpy(group + (4 - length), token, length);
// Parse each group of two hex digits into one octet
if ((octet = parse_octet(group)) == -1) {
return FAILURE;
} else {
dst_ip->s6_addr[index++] = (uint8_t)octet;
}
// Next octet in the group
if ((octet = parse_octet(group + 2)) == -1) {
return FAILURE;
} else {
dst_ip->s6_addr[index++] = (uint8_t)octet;
}
}
// colon was at the : (now the \0), so set
// it one passed that for the next token
// (only if colon is not null, then we break now)
if (colon) token = ++colon;
else break;
}
return SUCCESS;
}
/*
* This function initializes an ethernet header
*
* @param hdr A pointer to the ethernet header
* @param dst_mac A pointer to a buffer that contains the destination mac
* @param src_mac A pointer to a buffer that contains the mac of this host
* @param ethertype The ethernet type in host byte order
*/
void init_ether_header(struct ether_header *hdr,
const ether_addr dst_mac,
const ether_addr src_mac,
uint16_t ethertype) {
memcpy(hdr->ether_dhost, dst_mac, ETH_ALEN);
memcpy(hdr->ether_shost, src_mac, ETH_ALEN);
hdr->ether_type = ethertype;
}
/*
* This function initializes an ipv6 header
* This encompasses the version and everything given as parameter
*
* @param hdr A pointer to the ip header
* @param dst_ip A pointer to a buffer that contains the destination ip
* @param src_ip A pointer to a buffer that contains the ip of this host
* @param next The next hop field for this header
* @param plen The size of the ip payload in host byte order
* @param hoplimit The hoplimit for the ip packet
*/
void init_ip6_header(struct ipv6_hdr *hdr,
const struct in6_addr *dst_ip,
const struct in6_addr *src_ip,
uint8_t next,
uint16_t plen,
uint8_t hoplimit) {
hdr->version = IPv6_VERSION;
hdr->tc1 = hdr->tc2 = 0x00;
hdr->flow_label1 = hdr->flow_label2 = 0x00;
hdr->plen = plen;
hdr->nxt = next;
hdr->hlim = hoplimit;
hdr->src = *src_ip;
hdr->dst = *dst_ip;
}
void init_icmp6_header(struct icmp6_header *header) {
// ND_NEIGHBOR_SOLICIT = 0x87 (from netinet/icmp6.h)
header->type = ICMPv6_ND_SOLICIT_TYPE;
header->code = ICMPv6_ND_CODE;
}
void init_icmp6_checksum(struct ipv6_hdr *ip_header,
struct neighbor_solicit_payload* payload) {
payload->icmp6.checksum = icmp6_checksum((struct ip6_hdr*)ip_header,
(uint8_t*)payload,
sizeof(struct neighbor_solicit_payload));
}
void init_nd_option(struct nd_option *option,
const ether_addr source_link_address) {
// ND_OPT_SOURCE_LINKADDR = 0x01 (from netinet/icmp6.h)
option->header.type = ND_OPT_SOURCE_LINKADDR;
// Length is set to 0x01, but measured in 8 bytes
option->header.length = ND_OPTION_LENGTH;
// The 48-bit/6-byte source link (MAC) address
memcpy(option->link_address, source_link_address, ETH_ALEN);
}
/*
* This function initializes the icmpv6 header and the neighbor discovery
* payload
*
* @param payload A pointer to the buffer in which to initialize
* @param dst_ip A pointer to a buffer that contains the destination ip
* @param src_mac A pointer to a buffer that contains the mac of this host
*/
void init_icmp6_ndisc(struct neighbor_solicit_payload *payload,
const struct in6_addr *dst_ip,
const ether_addr src_mac) {
init_icmp6_header(&payload->icmp6);
payload->reserved = 0x00;
payload->target_address = *dst_ip;
init_nd_option(&payload->option, src_mac);
}
void init_packet(struct icmp6_neighbor_solicit *packet,
const ether_addr dst_mac,
const ether_addr src_mac,
const struct in6_addr *solicited_dst_ip,
const struct in6_addr *dst_ip,
const struct in6_addr *src_ip) {
memset(packet, 0, sizeof(*packet));
init_ether_header(&packet->ether, dst_mac, src_mac, IPv6_ETHER_TYPE);
init_ip6_header(&packet->ip,
solicited_dst_ip,
src_ip,
ICMPv6_NEXT_HEADER,
htons(MIN_ICMPv6_PAYLOAD_LENGTH),
ND_HOP_LIMIT);
init_icmp6_ndisc(&packet->payload, dst_ip, src_mac);
init_icmp6_checksum(&packet->ip, &packet->payload);
}
/*
* This function checks whether the ethernet header may be for a neighbor
* advertisement which is sent as response to our neighbor discovery
*
* @param hdr A pointer to the header
* @param len The length of the packet reported by read
* @param mymac A pointer to a buffer which contains the mac of this host
*
* @return 0 on fail
* 1 on success
*/
int is_my_ether_header(struct ether_header *hdr,
size_t len,
const ether_addr mymac) {
if (len < MIN_PACKET_LENGTH) return FAILURE;
// Should be an IPv6 ethertype
if (hdr->ether_type != IPv6_ETHER_TYPE) return FAILURE;
// Destination should be our MAC Address
if (memcmp(hdr->ether_dhost, mymac, ETH_ALEN) != 0) return FAILURE;
return SUCCESS;
}
/*
* This function checks whether the ip header is a valid ipv6 header and if
* it might be a neighbor advertisement sent in response to our neighbor
* discovery
*
* @param hdr A pointer to the ethernet payload
* @param len The length of the *packet* reported by read
* @param myip A pointer to a buffer containing the ip address of this host
* @param dst_ip A pointer to a buffer containing the ip address of our target
*
* @return 0 on fail
* 1 on success
*/
int is_my_ip6_header(struct ipv6_hdr *hdr,
size_t len,
const struct in6_addr *myip,
const struct in6_addr *dst_ip) {
// Length of the whole IPv6 packet should match
if (len < MIN_IPv6_TOTAL_LENGTH) return FAILURE;
if (hdr->version != IPv6_VERSION) return FAILURE;
if (hdr->nxt != ICMPv6_NEXT_HEADER) return FAILURE;
// The payload length should match for an ICMPv6 + ND Option packet
if (ntohs(hdr->plen) < MIN_ICMPv6_PAYLOAD_LENGTH) return FAILURE;
// The icmp6 payload can have more than one option, but then
// the payload length minus all the non-option stuff (e.g. icmp6 header)
// should be in multiples of 8 byte. Note that sizeof() the advertise
// message does not measure the options, because they are a flexible array
if (ntohs(hdr->plen) - sizeof(struct neighbor_advertise_payload) % 8 != 0)
// Hop Limit is fixed as 0xFF for neighbor discovery
if (hdr->hlim != ND_HOP_LIMIT) return FAILURE;
// Check the IPv6 addresses
// Source should be the address of the host we wanted the MAC address for
if ((memcmp(&hdr->src, dst_ip, sizeof(struct in6_addr))) != 0) return FAILURE;
// Destination should be our address
if ((memcmp(&hdr->dst, myip, sizeof(struct in6_addr))) != 0) return FAILURE;
return SUCCESS;
}
/*
* Checks the ICMPv6 header of the packet's payload, taking care of
* the type, code and especially the checksum of the header.
*
* @param packet The ND advertisement packet.
* @param payload The advertisement payload (header + nd option).
* @param len The length of the *payload*.
*
* @return 0 on fail
* 1 on success
*/
int is_my_icmp6_header(struct icmp6_neighbor_advertise* packet,
struct neighbor_advertise_payload* payload,
size_t len) {
int checksum;
int expected_checksum;
// Length of the ICMPv6 payload should be at least this
if (len < MIN_ICMPv6_PAYLOAD_LENGTH) return FAILURE;
// Type should be for an ND Advertisement
// ND_NEIGHBOR_ADVERT = 0x88 (from netinet/icmp6.h)
if (payload->icmp6.type != ICMPv6_ND_ADVERT_TYPE) return FAILURE;
if (payload->icmp6.code != ICMPv6_ND_CODE) return FAILURE;
// Need to store the checksum first, then set to null, then recompute it
checksum = payload->icmp6.checksum;
payload->icmp6.checksum = 0;
expected_checksum = icmp6_checksum((struct ip6_hdr*)&packet->ip,
(uint8_t*)payload,
len);
if (checksum != expected_checksum) return FAILURE;
return SUCCESS;
}
/**
*
* Checks the options of a received ICMPv6 payload.
*
* @param packet The ND advertisement packet.
* @param option An output pointer for the correct option (if there are many options).
*
*/
int is_my_nd_option(struct icmp6_neighbor_advertise* packet,
struct nd_option** option) {
uint8_t* iterator;
uint8_t* end;
int option_bytes;
option_bytes = get_nd_option_bytes(packet);
// Should be multiples of 8
if (option_bytes % 8 != 0) return FAILURE;
iterator = packet->payload.options;
end = iterator + option_bytes;
while (iterator < end) {
struct icmp_option_header* header = (struct icmp_option_header*)iterator;
// Won't be able to parse the other options if we don't
// know where they start because we don't know when this
// option ends
if ((header->length * 8) > option_bytes) return FAILURE;
// ND_OPT_TARGET_LINKADDR = 0x02 (from netinet/icmp6.h)
if (header->type == ND_OPT_TARGET_LINKADDR) {
// Length should 1, measured in 8 bytes.
if (header->length == ND_OPTION_LENGTH) {
// The advertised MAC address and the source MAC
// of the ethernet header should match
uint8_t* first = iterator + sizeof(struct icmp_option_header);
uint8_t* second = packet->ether.ether_shost;
if (memcmp(first, second, ETH_ALEN) == 0) {
*option = (struct nd_option*)iterator;
return SUCCESS;
}
}
}
iterator += (header->length * 8);
}
// No suitable option found
return FAILURE;
}
/**
* This function checks whether the ip payload is a
* neighbor advertisement sent to us.
*
* @param payload A pointer to the icmp payload
* @param len The length of the icmp payload
* @param dstip A pointer to a buffer containing the target ip
* @param option An output pointer for the correct option (if there are many options).
*
* @return 0 on fail
* 1 on success
*/
int is_my_neighbor_advertisement(struct icmp6_neighbor_advertise* packet,
size_t len,
const struct in6_addr *dstip,
struct nd_option** option) {
struct neighbor_advertise_payload* payload = &packet->payload;
if (!is_my_icmp6_header(packet, payload, len)) return FAILURE;
// Ensure the advertisement is a response to our solicitation
if (!is_set(payload, SOLICITED_FLAG)) return FAILURE;
// Ensure the target IP Address is the one who's solicited node
// multicast group we sent the packet to.
if (memcmp(&payload->target_address, dstip, IPv6_ADDR_LENGTH) != 0) {
return FAILURE;
}
if (!is_my_nd_option(packet, option)) return FAILURE;
return SUCCESS;
}
/*
* This function checks if the frame received is a neighbor advertisement sent
*as
* response to our neighbor discovery. If it is, it returns a pointer to the mac
* address in the frame.
* It calls the more specialized functions in succession to check their parts
* and does tests that have to be with access to multiple layers
* (i.e. checksum, matching macs)
*
* @param buffer A pointer to the received frame
* @param len The size of the received packet
* @param mymac A pointer to a buffer which contains the mac of this host
* @param myip6 A pointer to a buffer which contains the ip of this host
* @param dstip A pointer to a buffer which contains the destination ip
*
* @return null on fail
* A pointer to the destination mac address sent with the neighbor
* advertisement
*/
ether_addr retrieve_mac(uint8_t *buffer,
ssize_t len,
const ether_addr mymac,
const struct in6_addr *myip6,
const struct in6_addr *dstip) {
struct nd_option* option;
struct icmp6_neighbor_advertise *packet =
(struct icmp6_neighbor_advertise *)buffer;
if (!is_my_ether_header(&packet->ether, len, mymac)) {
return NULL;
}
len -= sizeof(struct ether_header);
if (!is_my_ip6_header(&packet->ip, len, myip6, dstip)) {
return NULL;
}
len -= sizeof(struct ipv6_hdr);
// Note that we pass the option as an output parameter, it will
// then store the option that contains the requested link address
// for the case that there are many options (and if the advertisement
// is valid at all).
if (!is_my_neighbor_advertisement(packet, len, dstip, &option)) {
return NULL;
}
// Wooh!
return option->link_address;
}
void get_solicited_ip(const struct in6_addr *ip, struct in6_addr *solicited) {
size_t index = 0;
// ff02:
solicited->s6_addr[index++] = 0xff;
solicited->s6_addr[index++] = 0x02;
// 8 bytes (4 groups) for the ::
memset(solicited->s6_addr + index, 0, 8);
index += 8;
// :0001
solicited->s6_addr[index++] = 0x00;
solicited->s6_addr[index++] = 0x01;
// :ff
solicited->s6_addr[index++] = 0xff;
// Lower 24 bits (3 bytes) of the IPv6 Address
memcpy(solicited->s6_addr + index, ip->s6_addr + index, 3);
}
void get_solicited_mac(const struct in6_addr *ip, ether_addr mac) {
// IPv6 multicast prefix
mac[0] = mac[1] = 0x33;
// The last 4 bytes of the solicited node IP address
memcpy(mac + 2, ip->s6_addr + 12, 4);
}
void send_packet(int fd, const struct icmp6_neighbor_solicit *packet, size_t length) {
hexdump(packet, sizeof(*packet));
if (grnvs_write(fd, packet, length) < 0) {
fprintf(stderr, "grnvs_write() failed: %s\n", strerror(errno));
}
}
ether_addr
read_mac(int fd, uint8_t* recbuffer, const ether_addr mymac, const struct in6_addr *sip6, const struct in6_addr *dip6, unsigned int timeout) {
ssize_t ret;
ether_addr mac;
while ((ret = grnvs_read(fd, recbuffer, RECEIVE_BUFFER_LENGTH, &timeout))) {
if ((mac = retrieve_mac(recbuffer, ret, mymac, sip6, dip6))) {
break;
}
}
if (ret == 0) {
/*
* This should go to stdout because the tester uses this to
* determine a graceful shutdown. Do NOT change this
*/
fprintf(stdout, "Timeout\n");
return NULL;
}
return mac;
}
/*====================================TODO===================================*/
/*
*TODO: Print the retrieved mac address.
* The format MUST strictly adhere to the following rule:
*
* <IP-Address> is at <mac>
*
* The IP-address MAY be shortened, the mac MUST be 2 characters for
* each byte. The result MUST be printed on stdout. Don't forget the
* newline!
*
* Example output:
* ::1 is at 01:02:03:04:05:06
*/
/*===========================================================================*/
void print_mac(const char *ip, ether_addr mac) {
printf("%s is at %.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
ip,
(unsigned)mac[0],
(unsigned)mac[1],
(unsigned)mac[2],
(unsigned)mac[3],
(unsigned)mac[4],
(unsigned)mac[5]);
}
void assignment3(int fd, const char *ipaddr, const int timeoutval) {
// Buffer for receiving packets
uint8_t recbuffer[RECEIVE_BUFFER_LENGTH];
// Returned target MAC
ether_addr mac;
struct icmp6_neighbor_solicit packet;
size_t length = sizeof(packet);
struct in6_addr dip6;
const struct in6_addr *sip6 = grnvs_get_ip6addr(fd);
struct in6_addr solicited_dip6;
// Buffer for solicited node multicast mac
uint8_t dstmac[ETH_ALEN];
const ether_addr mymac = (const ether_addr)grnvs_get_hwaddr(fd);
if (parse_ip(ipaddr, &dip6) == FAILURE) {
fprintf(stderr,
"Wrong input format for destination address, "
"input should be in format: a:b:c::1:2:3\n");
return;
}
get_solicited_ip(&dip6, &solicited_dip6);
get_solicited_mac(&solicited_dip6, dstmac);
init_packet(&packet, dstmac, mymac, &solicited_dip6, &dip6, sip6);
send_packet(fd, &packet, length);
mac = read_mac(fd, recbuffer, mymac, sip6, &dip6, timeoutval * 1000);
if (mac) print_mac(ipaddr, mac);
}
int main(int argc, char **argv) {
struct arguments args;
int sock;
if (parse_args(&args, argc, argv) < 0) {
fprintf(stderr,
"Failed to parse arguments, call with "
"--help for more information\n");
return -1;
}
if ((sock = grnvs_open(args.interface, SOCK_RAW)) < 0) {
fprintf(stderr, "grnvs_open() failed: %s\n", strerror(errno));
return -1;
}
assignment3(sock, args.dst, args.timeout);
grnvs_close(sock);
return 0;
}
@omarcosr
Copy link

omarcosr commented Feb 5, 2022

Is there a possibility to make the remaining files available?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment