일상 메모장

sk_buff 에서 Mac header, IP v4 and v6 header, TCP header 파싱 본문

Kernel TCP IP

sk_buff 에서 Mac header, IP v4 and v6 header, TCP header 파싱

핸드오버 2019. 12. 28. 09:23

안녕하세요.

 

오늘은 커널내부에서 네트워크 패킷을 관리하는 구조체인 sk_buff structure 를 각 네트워크 헤더별로

파싱하는 방법을 포스팅하겠습니다.

 

우선 각 함수를 설명하기 전에 기본적으로 sk_buff 의 멤버 변수들을 먼저 확인해보도록 하겠습니다.

sk_buff 는 headroom, tailroom, 그리고 실제 데이터가 들어있는 영역이 아래와 같이 있습니다.

이 위치를 표시하기 위해 아래와 같이 head, data, tail, end 변수를 사용합니다.

head ~ data 는 head room.

data ~ tail 은 실제 데이터가 들어있는 room

tail ~ end 는 tail room 입니다.

그리고 sk_buff 구조체는 mac header, network header(ex:IP), transport header(ex: TCP, UDP)의 offset을 기억하는

mac_header, network_header, transport_header를 멤버 변수로 가지고 있습니다.

 

Ethernet header

  • ethhdr (ethernet header) 구조체
/* linux/if_ether.h 에 위치 */
struct ethhdr {
	unsigned char	h_dest[ETH_ALEN];	/* destination eth addr	*/
	unsigned char	h_source[ETH_ALEN];	/* source ether addr	*/
	__be16			h_proto;			/* packet type ID field	*/
} __attribute__((packed));
  • eth_hdr(), skb_mac_header() 함수 선언부
#include <linux/if_ether.h>

struct ethhdr *eth_hdr(const struct sk_buff *skb);

/* 또는 */

#include <linux/skbuff.h>

unsigned char *skb_mac_header(const struct sk_buff *skb);
  • 설명
    • eth_hdr() 내부에서는 실제로 skb_mac_header() 함수를 호출하는 구조입니다.
    • 내부 구현은 sk_buff 의 시작위치인 skb->head 에 skb->mac_header 의 offset 위치를 더한값을 리턴합니다.
      skb->head + skb->mac_header

IP header v4 and v6(Network layer)

IPv4 관련

  • iphdr 구조체
#include <linux/ip.h>

struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	__u8	ihl:4,
    		version:4;
#else defined (__BIG_ENDIAN_BITFIELD)
	__u8	version:4,
    		ihl:4;
#else
#error	"Please fix <asm/byteorder.h>"
#endif
	__u8	tos;
    __be16	tot_len;
    __be16	id;
    __be16	frag_off;
    __u8	ttl;
    __u8	protocol;
    __sum16	check;
    __be32	saddr;
    __be32	daddr;
    /*The options start here. */
};
  • ip_hdr(), skb_network_header() 함수 선언부
#include <linux/ip.h>

struct iphdr *ip_hdr(const struct sk_buff *skb);

/* 또는 */

#include <linux/skbuff.h>

unsigned char *skb_network_header(const struct sk_buff *skb);
  • 설명
    • ip_hdr() 내부에서는 실제로 skb_network_header() 함수를 호출하는 구조입니다.
    • 내부 구현은 sk_buff 의 시작위치인 skb->head 에 skb->network_header 의 offset 위치를 더한값을 리턴합니다.
      skb->head + skb->network_header

IPv6 관련

  • ipv6hdr
#include <linux/ipv6.h>

struct ipv6hdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	__u8		priority:4,
    			version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
	__u8		version:4,
    			priority:4;
#else
#error	"Please fix <asm/byteorder.h>"
#endif
	__u8		flow_lbl[3];
    __be16		payload_len;
    __u8		nexthdr;
    __u8		hop_limit;
    
    struct	in6_addr	saddr;
    struct	in6_addr	daddr;
};
  • ipv6_hdr(), skb_network_header() 함수 선언부
#include <linux/ipv6.h>

struct ipv6hdr *ipv6_hdr(const struct sk_buff *skb);

/* 또는 */

#include <linux/skbuff.h>

unsigned char *skb_network_header(const struct sk_buff *skb);
  • 설명
    • ip_hdr() 내부에서는 실제로 skb_network_header() 함수를 호출하는 구조입니다.
    • 내부 구현은 sk_buff 의 시작위치인 skb->head 에 skb->network_header 의 offset 위치를 더한값을 리턴합니다.
      skb->head + skb->network_header
    • 그런데 실제로 파싱할때는 network header가 IPv4인지 IPv6 인지 알지 모르기때문에 ip_hdr()로 먼저 ip header를 얻어낸후 iphdr 와 ipv6hdr 구조체의 처음 version, priority의 사이즈가 같기 때문에, iphdr->version 이
      4면 그대로 사용하면 되고, iphdr->version 이 6이면 ipv6_hdr() 함수를 사용해서 다시 파싱하면 됩니다.

TCP header (Transport layer)

  • tcphdr 구조체
  •  
#include <linux/tcp.h>

struct tcphdr {
	__be16	source;
    __be16	dest;
    __be32	seq;
    __be32	ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)
	__u16	res1:4,
    		doff:4,
            fin:1,
            syn:1,
            rst:1,
            psh:1,
            ack:1,
            urg:1,
            ece:1,
            cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
	__u16	doff:4,
    		res1:4,
            cwr:1,
            ece:1,
            urg:1,
            ack:1,
            psh:1,
            rst:1,
            syn:1,
            fin:1;
#else
#error	"Adjust your <asm/byteorder.h> defines"
#endif
	__be16	window;
    __sum16	check;
    __be16	urg_ptr;
}
  • tcp_hdr(), skb_mac_header() 함수 선언부
#include <linux/tcp.h>

struct tcphdr *tcp_hdr(const struct sk_buff *skb);

/* 또는 */

#include <linux/skbuff.h>

unsigned char *skb_transport_header(const struct sk_buff *skb);
  • 설명
    • tcp_hdr() 내부에서는 실제로 skb_transport_header() 함수를 호출하는 구조입니다.
    • 내부 구현은 sk_buff 의 시작위치인 skb->head 에 skb->tranport_header 의 offset 위치를 더한값을 리턴합니다.
      skb->head + skb->transport_header

오늘 포스팅은 여기서 마치겠습니다. 다음에는 위의 함수를 사용하지 않고,

각 헤더들의(mac header, ip header, tcp header) 멤버들을 사용해서 헤더를 가져오는 방법을 포스팅하겠습니다.

그 전에 각 헤더들의 역할에 대해서 설명하는게 먼저 있는게 좋겠네요 ㅎㅎ

 

읽어주셔서 감사합니다.

'Kernel TCP IP' 카테고리의 다른 글

TCP header (+ TCP 옵션)  (0) 2019.12.31
TCP timers (Persistent, Keep alive, Time wait)  (0) 2019.12.27
TCP timer (Retransmission timer)  (0) 2019.12.26
TCP sliding window 개념  (0) 2019.12.21
struct sk_buff 관련 함수  (1) 2019.12.20