Skip to content

Commit

Permalink
working ethernet frame parsing!
Browse files Browse the repository at this point in the history
  • Loading branch information
jimfangx committed May 24, 2024
1 parent 9227d49 commit 1e40ddf
Show file tree
Hide file tree
Showing 15 changed files with 299 additions and 10 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/output
/bin
*.o
5 changes: 4 additions & 1 deletion .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"/usr/include/x86_64-linux-gnu"
"/usr/include/x86_64-linux-gnu",
"${workspaceFolder}/include",
"/usr/include",
"/usr/lib/gcc/x86_64-linux-gnu/13/include"
],
"defines": [],
"cStandard": "c17",
Expand Down
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
"stdlib.h": "c",
"prctl.h": "c",
"tuntap_interface.h": "c",
"types.h": "c"
"types.h": "c",
"inet.h": "c",
"syshead.h": "c",
"if.h": "c"
}
}
21 changes: 21 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Compiler
CC := gcc

# Compiler flags
CFLAGS := -Wall -Wextra -I include/

# Source files
SRCS := $(wildcard src/*.c)
OBJS := $(SRCS:.c=.o)

# Target executable
TARGET := bin/main

# Build the executable
$(TARGET): $(OBJS)
$(CC) $(CFLAGS) -o $(TARGET) $(OBJS)


# Clean up object files and the executable
clean:
rm -f $(OBJS) $(TARGET)
66 changes: 66 additions & 0 deletions include/dl_list.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
Implementation of a doubly linked list for skbuff (*next/*prev
elements) seen in the linux src here:
https://lxr.linux.no/linux+v2.6.20/include/linux/skbuff.h#L184
See this diagram from 61B for the implementation:
https://docs.google.com/presentation/d/1jjivjdvD4mx6qb4bd4rUKB6FDZbYtveJ5NKeyVeA1lk/edit#slide=id.g829fe3f43_0_376
https://github.com/Berkeley-CS61B-Student/sp24-s1085/blob/main/proj1a/src/LinkedListDeque61B.java
*/

#ifndef _LIST_H
#define _LIST_H

#include <stddef.h>

struct list_head {
struct list_head *next;
struct list_head *prev;
};

#define LIST_HEAD(name) struct list_head name = {&(name), &(name)}

// define all static inline functions in the header file:
// https://stackoverflow.com/questions/5526461/gcc-warning-function-used-but-not-defined

static inline void list_init(struct list_head *head) {
head->next = head;
head->prev = head;
}

static inline void add_first(struct list_head *new_element, struct list_head *head) {
new_element->next = head->next;
new_element->prev = head;
head->next->prev = new_element;
head->next = new_element;
}

static inline void add_last(struct list_head *new_element, struct list_head *head) {
new_element->next = head;
new_element->prev = head->prev;
head->prev->next = new_element;
head->prev = new_element;
}

static inline void remove_elem(struct list_head *elem_to_remove) {
elem_to_remove->prev->next = elem_to_remove->next;
elem_to_remove->next->prev = elem_to_remove->prev;
}

static inline int is_list_empty(struct list_head *head) {
return head->next == head;
}

#define get_list_item(ptr, type, member) \
((type *)((char *)(ptr) - offsetof(type, member)))

#define get_list_first_entry(ptr, type, member) \
get_list_item((ptr)->next, type, member)

#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)

#endif
47 changes: 47 additions & 0 deletions include/eth.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
defs for ethernet frame struct & code
The following is helpful for interpreting our internet frame.
This data should come from our TAP driver
https://medium.com/kernel-space/unpacking-a-raw-packet-ethernet-frame-part-1-e91033e745a4
- Heres a chart! Note that preamble is "omitted"
We will be implementing 802.3 frame structure
Here is the original standard:
https://ethernethistory.typepad.com/papers/EthernetSpec.pdf (page 27)
Linux provides a struct for the ethernet frame header as a guide also:
https://github.com/torvalds/linux/blob/master/include/uapi/linux/if_ether.h#L174
https://stackoverflow.com/questions/35306080/parsing-ethernet-frames-and-data-types
*/

#ifndef ETH_H_
#define ETH_H_

#include "syshead.h"
#include "skbuff.h"

#define ETH_ALEN 6

struct eth_hdr {
u_int8_t ether_dhost[ETH_ALEN]; // destination mac
u_int8_t ether_shost[ETH_ALEN]; // source mac
u_int16_t ether_type;
// uint8_t payload[]; // not in official implementation
} __attribute__ ((packed));


static inline struct eth_hdr *unpack_eth_hdr(struct sk_buff *skb) {
struct eth_hdr *eth_hdr = (struct eth_hdr *)(skb->head);
// convert ether_type from big endian -> small endian
eth_hdr->ether_type = ntohs(eth_hdr->ether_type);
return eth_hdr;
};


#endif
8 changes: 8 additions & 0 deletions include/net_dir.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef NET_DIR_H
#define NET_DIR_H

#include "syshead.h"

void net_dir_receive();

#endif
Empty file added include/route.h
Empty file.
35 changes: 35 additions & 0 deletions include/skbuff.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
what we need in our skbuff is documented here:
https://dev.to/amrelhusseiny/linux-networking-part-1-kernel-net-stack-180l
https://lxr.linux.no/linux+v2.6.20/include/linux/skbuff.h#L184
*/
#ifndef SK_BUFF_H
#define SK_BUFF_H

#include "dl_list.h"
#include "route.h"
#include "net_dir.h"
#include <pthread.h>
#include <stdint.h>

struct sk_buff {
struct list_head list; // implements *next *prev
// struct routing_table_entry *rt;
// struct outgoing_eth_pack_meta *dev;
uint32_t interface;
uint16_t protocol; // 2 byte identifier
uint8_t *head;
uint8_t *data;
uint8_t *tail;
uint8_t *end;
};

struct sk_buff_head {
struct list_head head;
int len;
};

struct sk_buff *alloc_skb(unsigned int size);

#endif
1 change: 1 addition & 0 deletions headers/syshead.h → include/syshead.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <sys/un.h>
#include <time.h>
#include <unistd.h>
#include <linux/if_ether.h>

#endif

Expand Down
File renamed without changes.
11 changes: 11 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
# TCP



## Include path
```
${workspaceFolder}/**
/usr/include/x86_64-linux-gnu
${workspaceFolder}/include
/usr/include
/usr/lib/gcc/x86_64-linux-gnu/13/include
```
18 changes: 11 additions & 7 deletions src/main.c
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
#include "syshead.h"
#include "tuntap_interface.h"
#include "net_dir.h"

int RUNNING = 1;

int main() {
tun_init();

char buf[1600];

while (1) {
int read_bytes = tun_read(buf, sizeof(buf));
printf("read bytes: %d\n", read_bytes);
printf("read values: %x\n", buf);
}
// char buf[1600];

// while (1) {
// int read_bytes = tun_read(buf, sizeof(buf));
// printf("read bytes: %d\n", read_bytes);
// printf("read values: %x\n", buf);
// }
// free_tun();

net_dir_receive();

return 0;
}
58 changes: 58 additions & 0 deletions src/net_dir.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
This file will handle ethernet frame receive parsing & transmit
we need to implement both incoming & outgoing. incoming is a bit easier to implement imo
for receiving/incoming:
- each incoming packet gets stored in a new allocated sk_buff - data will live in sk_buff as it gets passed up the pipeline
*/
#include "syshead.h"
#include "tuntap_interface.h"
#include "skbuff.h"
#include "eth.h"

// https://ethernethistory.typepad.com/papers/EthernetSpec.pdf - page 32
#define MAX_FRAME_SIZE 1600

extern int RUNNING;


void net_dir_receive() {
while (RUNNING) {
/*
allocate skbuff
read from tap + check for error read
if err, free skbuff, end
otherwise:
cast to eth struct defined in eth.h (eth.h will take care of big endian -> little endian)
look at ethertype, send to the correct place (ARP/IP)
*/

struct sk_buff *skb = alloc_skb(MAX_FRAME_SIZE);

if (tun_read((char *) skb->data, MAX_FRAME_SIZE) < 0) {
perror("err reading from tun_read");
free(skb);
// return NULL;
exit(1);
}

struct eth_hdr *header = unpack_eth_hdr(skb);

if (header->ether_type == ETH_P_ARP) { // direct to ARP receive

printf("DIRECTING TO ARP\n");
// print the ARP packet
printf("ARP PACKET\n");
printf("SRC MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", header->ether_shost[0], header->ether_shost[1], header->ether_shost[2], header->ether_shost[3], header->ether_shost[4], header->ether_shost[5]);
printf("DEST MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", header->ether_dhost[0], header->ether_dhost[1], header->ether_dhost[2], header->ether_dhost[3], header->ether_dhost[4], header->ether_dhost[5]);

} else if (header->ether_type == ETH_P_IP) { // direct to IP receive

printf("DIRECTING TO IP\n");

}

}

}
31 changes: 31 additions & 0 deletions src/skbuff.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
Reimplementation of Linux's skbuff data structure.
https://docs.kernel.org/networking/skbuff.html
struct defs: https://docs.kernel.org/networking/kapi.html#c.sk_buff
why do we need skbuff? can't we just pass our raw data into some raw thing?
https://wiki.linuxfoundation.org/networking/sk_buff#:~:text=However%2C%20sk_buff%20provides%20an%20additional,need%20to%20be%20copied%20around.
essentially:
we are reimplementing a doubly-linked list + the additional functions that skbuff implements (ex: skb_put(), skb_put(), etc)
- for the doubly linked list implementation, see dl_list.c/h
skbuff will be the main carrier that carries our network data through the networking layers.
*/

#include "skbuff.h"
#include "dl_list.h"

struct sk_buff *alloc_skb(unsigned int size) {
struct sk_buff *skb = calloc(1, sizeof(struct sk_buff));
skb->head = malloc(size);
skb->data = skb->head;
skb->tail = skb->head;
skb->end = skb->head + size;

list_init(&skb->list);

return skb;
}

0 comments on commit 1e40ddf

Please sign in to comment.