반응형

 


 

작성일: 2023년 10월 10일

 

패킷 보내기 (Send a packet)

예제 코드

/**
 * How to build
 *  $  gcc send-raw-packet.c -o send-raw-packet
 *
 * How to run
 *  $ ./send-raw-packet
 *    or
 *  $ ./send-raw-packet  eth0
 */

#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <linux/ip.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <errno.h>


// FIXME: MY_DEST_MACX 값은 각자 테스트 환경이 맞게 수정해서 사용.
#define MY_DEST_MAC0	0x52
#define MY_DEST_MAC1	0x54
#define MY_DEST_MAC2	0x00
#define MY_DEST_MAC3	0xcf
#define MY_DEST_MAC4	0xab
#define MY_DEST_MAC5	0x76

// FIXME: DEFAULT_IF 값은 각자 테스트 환경이 맞게 수정해서 사용.
#define DEFAULT_IF	"enp7s0"

#define BUF_SIZ		1024


int main(int argc, char *argv[])
{
	int                  sockfd;
	int                  tx_len = 0;
	char                 sendbuf[BUF_SIZ];
	char                 ifName[IFNAMSIZ];
	struct ifreq         if_idx;
	struct ifreq         if_mac;
	struct ether_header  *eh = (struct ether_header *) sendbuf;
	struct iphdr         *iph = (struct iphdr *) (sendbuf + sizeof(struct ether_header));
	struct sockaddr_ll   socket_address;

	// Network interface name 지정하기 (예: eth0)
	if (argc > 1)
	{
		strcpy(ifName, argv[1]);
	}
	else
	{
		strcpy(ifName, DEFAULT_IF);
	}

	// RAW socket 사용을 위한 File descriptor 생성하기
	if ((sockfd = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW)) == -1)
	{
		printf("socket() error: %d (%s)\n", errno, strerror(errno));
		return 0;
	}

	// Network interface의 index 값 구하기
	memset(&if_idx, 0, sizeof(struct ifreq));
	strncpy(if_idx.ifr_name, ifName, IFNAMSIZ-1);
	if (ioctl(sockfd, SIOCGIFINDEX, &if_idx) < 0)
	{
		printf("ioctl(SIOCGIFINDEX, %s) error: %d (%s)\n", ifName, errno, strerror(errno));
		return 0;
	}

	// Network interface의 MAC address 구하기
	memset(&if_mac, 0, sizeof(struct ifreq));
	strncpy(if_mac.ifr_name, ifName, IFNAMSIZ-1);
	if (ioctl(sockfd, SIOCGIFHWADDR, &if_mac) < 0)
	{
		printf("ioctl(SIOCGIFHWADDR, %s) error: %d (%s)\n", ifName, errno, strerror(errno));
		return 0;
	}

	// Ehternet header 구성하기 (참고: sendbuf pointer가 eh 주소를 pointing)
	memset(sendbuf, 0, BUF_SIZ);
	/*
	 * ioctl() 함수를 이용해서 얻은 'enp7s0' NIC에 대한 MAC Address 값을
	 * ethernet header 구조체의 ether_shost 변수에 복사한다.
	 */
	printf("( %s )  MAC address = ", ifName);
	for (int idx = 0; idx < 6; idx++)
	{
		eh->ether_shost[idx] = ((uint8_t *)&if_mac.ifr_hwaddr.sa_data)[idx];
		printf("%02x", eh->ether_shost[idx]);
		if (idx < 5)
		{
			printf(":");
		}
	}
	printf("\n");

	/*
	 * 일반적으로 NIC port의 MAC address를 ethernet frame의 source address로 사용하지만
	 * Ethernet packet 전송 테스트를 위해서 가짜 Source MAC address를 만들었다.
	 */
	eh->ether_shost[3] = 0x01;
	eh->ether_shost[4] = 0x02;
	eh->ether_shost[5] = 0x03;

	// Ethernet frame - Destination host MAC address
	eh->ether_dhost[0] = MY_DEST_MAC0;
	eh->ether_dhost[1] = MY_DEST_MAC1;
	eh->ether_dhost[2] = MY_DEST_MAC2;
	eh->ether_dhost[3] = MY_DEST_MAC3;
	eh->ether_dhost[4] = MY_DEST_MAC4;
	eh->ether_dhost[5] = MY_DEST_MAC5;

	// Ethertype:  Internet Protocol (0x0800)
	eh->ether_type = htons(ETH_P_IP);
	tx_len += sizeof(struct ether_header);

	// FIXME: IP Header
	//   각자 테스트 환경에 맞게 iph 변수를 수정하여 사용하기
  	iph->ihl = 20 >> 2;  // NOTE: (20 >> 2) * 4 = 20 bytes (IHL은 4 byte 단위로 해석되기 때문)
	iph->version = 4;
	iph->protocol = IPPROTO_IP;
	iph->saddr = inet_addr("10.1.1.10");
	iph->daddr = inet_addr("10.1.1.11");
	iph->tot_len = 46 + 32;

	tx_len += sizeof(struct iphdr);

	/* Payload (Packet data) */
	for (char idx = 0; idx < 32; idx++)
	{
		sendbuf[tx_len++] = idx;
	}

	/* Index of the network device */
	socket_address.sll_ifindex = if_idx.ifr_ifindex;
	/* Address length*/
	socket_address.sll_halen = ETH_ALEN;
	/* Destination MAC */
	socket_address.sll_addr[0] = MY_DEST_MAC0;
	socket_address.sll_addr[1] = MY_DEST_MAC1;
	socket_address.sll_addr[2] = MY_DEST_MAC2;
	socket_address.sll_addr[3] = MY_DEST_MAC3;
	socket_address.sll_addr[4] = MY_DEST_MAC4;
	socket_address.sll_addr[5] = MY_DEST_MAC5;

	/* Send packet */
	if (sendto(sockfd, sendbuf, tx_len, 0, (struct sockaddr*)&socket_address, sizeof(struct sockaddr_ll)) < 0)
	    printf("Send failed\n");

	return 0;
}

 

 

패킷 받기 (Receive a packet)

예제 코드

/**
 * How to build
 *  $  gcc recv-raw-packet.c -o recv-raw-packet
 *
 * How to run
 *  $ ./recv-raw-packet
 *    or
 *  $ ./recv-raw-packet  eth0
 */

#include <unistd.h>
#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <errno.h>

#define ETHER_TYPE	0x0800

#define DEFAULT_IF	"enp7s0"
#define BUF_SIZ		1024

int main(int argc, char *argv[])
{
	char sender[INET6_ADDRSTRLEN];
	int sockfd, ret;
	int sockopt;
	ssize_t pktbytes;
	struct ifreq ifopts;	// To set promiscuous mode
	struct ifreq if_ip;	    // To get IP address of this host NIC
	struct sockaddr_storage peer_addr;
	uint8_t buf[BUF_SIZ];
	char ifName[IFNAMSIZ];

	// Network interface name 지정하기 (예: eth0)
	if (argc > 1)
	{
		strcpy(ifName, argv[1]);
	}
	else
	{
		strcpy(ifName, DEFAULT_IF);
	}

	// Ethernet + IP + UDP header
	struct ether_header *eh = (struct ether_header *) buf;
	struct iphdr *iph = (struct iphdr *) (buf + sizeof(struct ether_header));
	struct udphdr *udph = (struct udphdr *) (buf + sizeof(struct iphdr) + sizeof(struct ether_header));

	if ((sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETHER_TYPE))) == -1)
	{
		printf("socket(PF_PACKET, SOCK_RAW, ETHER_TYPE) error: %d (%s)\n", errno, strerror(errno));
		return -1;
	}

	// Set interface to promiscuous mode
	strncpy(ifopts.ifr_name, ifName, IFNAMSIZ-1);
	ioctl(sockfd, SIOCGIFFLAGS, &ifopts);
	ifopts.ifr_flags |= IFF_PROMISC;
	ioctl(sockfd, SIOCSIFFLAGS, &ifopts);
	if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof sockopt) == -1) {
		printf("setsockopt(SO_REUSEADDR) error: %d (%s)\n", errno, strerror(errno));
		close(sockfd);
		exit(0);
	}

	// Bind to device
	if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE, ifName, IFNAMSIZ-1) == -1)	{
		printf("setsockopt(SO_BINDTODEVICE, %s) error: %d (%s)\n", ifName, errno, strerror(errno));
		close(sockfd);
		exit(0);
	}

repeat:
	printf("\nWaiting to recvfrom...\n");
	pktbytes = recvfrom(sockfd, buf, BUF_SIZ, 0, NULL, NULL);
	printf("  Got packet %lu bytes\n", pktbytes);

	printf("Destination  MAC address = ");
	for (int idx = 0; idx < 6; idx++)
	{
		printf("%02x", eh->ether_dhost[idx]);
		if (idx < 5)
		{
			printf(":");
		}
	}
	printf("\n");

	// Get source IP
	((struct sockaddr_in *)&peer_addr)->sin_addr.s_addr = iph->saddr;
	inet_ntop(AF_INET, &((struct sockaddr_in*)&peer_addr)->sin_addr, sender, sizeof sender);

	// Look up my device IP addr
	memset(&if_ip, 0, sizeof(struct ifreq));
	strncpy(if_ip.ifr_name, ifName, IFNAMSIZ-1);
	if (ioctl(sockfd, SIOCGIFADDR, &if_ip) >= 0) {
		printf("Source IP: %s\n My IP: %s\n", sender,
				inet_ntoa(((struct sockaddr_in *)&if_ip.ifr_addr)->sin_addr));
		// Ignore if I sent it
		if (strcmp(sender, inet_ntoa(((struct sockaddr_in *)&if_ip.ifr_addr)->sin_addr)) == 0)	{
			printf("but I sent it :(\n");
			ret = -1;
			goto done;
		}
	}

	/* UDP payload length */
	ret = ntohs(udph->len) - sizeof(struct udphdr);

	/* Print packet */
	printf("\tData:");
	for (int idx = 0; idx < pktbytes; idx++)
	{
		printf("%02x ", buf[idx]);
	}
	printf("\n");

done:
	goto repeat;

	close(sockfd);
	return ret;
}

 

 

 

 

 


 

반응형
테스트 및 블로그 작성한 날짜: 2023년 6월 13일

 

Prometheus 설치

아래 Web page에서 내 운영 환경에 맞는 파일을 다운로드한다.

https://prometheus.io/download/

 

아래와 같이 명령을 따라 수행하여 Prometheus 서버를 구동한다.

##
## Prometheus 서버 설치 파일을 다운로드
## 

$ wget https://github.com/prometheus/prometheus/releases/download/v2.45.0-rc.0/prometheus-2.45.0-rc.0.linux-amd64.tar.gz

##
## 압축 풀기
##

$ tar xf prometheus-2.45.0-rc.0.linux-amd64.tar.gz

$ cd prometheus-2.45.0-rc.0.linux-amd64

##
## 설정 파일 수정하기
##

$ cat prometheus.yml

# my global config
global:
  scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
  # scrape_timeout is set to the global default (10s).

# Alertmanager configuration
alerting:
  alertmanagers:
    - static_configs:
        - targets:
          # - alertmanager:9093

# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
  # - "first_rules.yml"
  # - "second_rules.yml"

# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
  # The job name is added as a label `job=<job_name>` to any timeseries scraped from this config.
  - job_name: "prometheus"

    # metrics_path defaults to '/metrics'
    # scheme defaults to 'http'.

    # 나는 아래 부분을 수정했다.
    # 10.1.4.56:8080은 Python으로 작성한 Exporter의 접속점이다.
    static_configs:
      - targets: ["10.1.4.56:8000", "10.1.3.241:9090"]
      
##
## Prometheus 서버 구동하기
##

$ ./prometheus --config.file="./prometheus.yml" &
... 중간 생략 ...
ts=2023-06-13T08:24:54.837Z caller=main.go:1004 level=info msg="Server is ready to receive web requests."
ts=2023-06-13T08:24:54.837Z caller=manager.go:995 level=info component="rule manager" msg="Starting rule manager..."

 

Prometheus Client 예제 작성

테스트용 Metric data를 만들기 위해서 아래와 같이 Example code를 작성한다.

from prometheus_client import start_http_server, Summary
from prometheus_client import Counter
import random
import time


# Create a metric to track time spent and requests made.
REQUEST_TIME = Summary('request_processing_seconds', 'Time spent processing request')

# Decorate function with metric.
@REQUEST_TIME.time()
def process_request(t):
    """A dummy function that takes some time."""
    time.sleep(t)


if __name__ == '__main__':
    # Start up the server to expose the metrics.
    start_http_server(8000)

    c = Counter('sejong_packet_bytes', 'http request failure', ['src_ip', 'dst_ip', 'src_port', 'dst_port'])
    # Generate some requests.
    while True:
        process_request(random.random()/10)
        c.labels(src_ip='10.1.4.11', dst_ip='192.168.5.22', src_port='11111', dst_port='').inc(1322)
        c.labels(src_ip='10.1.8.33', dst_ip='192.168.9.66', src_port='12345', dst_port='23456').inc(1500)
        c.labels(src_ip='172.16.8.33', dst_ip='192.168.9.66', src_port='12345', dst_port='23456').inc(1500)
        c.labels(src_ip='172.17.7.33', dst_ip='192.168.33.66', src_port='80808', dst_port='90909').inc(1500)

 

위에서 작성한 example code를 실행한다.

$ pip install prometheus-client

$ python3 my_example.py

 

좀 더 다양한 Python example code를 보려면, GitHub 저장소를 볼 것!

https://github.com/prometheus/client_python

 

Web UI에서 Metric 확인

Query 입력하는 공간에 "increase(sejong_packet_bytes_total[30s])" 를 입력한다.

 

 

블로그 작성자: sejong.jeonjo@gmail.com

 

반응형

 

OKD는 기술 지원을 받기 어려우니까, 직접 Source Code를 열어봐야 하는 일이 생긴다.

이런 경우, 아래 GitHub Repository에서 Git Clone해서 분석해보면 좋다.

 

 

 

OpenShift

The developer and operations friendly Kubernetes distro - OpenShift

github.com

 

반응형

 

Kubernetes SRIOV Network Device Plugin에 관해 Code Level 스터디를 하고 싶다면,

아래 2개의 문서를 읽어보라~~~

(지금 하고 있던 업무 끝내고, 빨리 읽고 정리해야겠다 ㅠㅠ)

 

 

 

GitHub - k8snetworkplumbingwg/sriov-network-device-plugin: SRIOV network device plugin for Kubernetes

SRIOV network device plugin for Kubernetes. Contribute to k8snetworkplumbingwg/sriov-network-device-plugin development by creating an account on GitHub.

github.com

 

 

 

GitHub - k8snetworkplumbingwg/sriov-cni: DPDK & SR-IOV CNI plugin

DPDK & SR-IOV CNI plugin. Contribute to k8snetworkplumbingwg/sriov-cni development by creating an account on GitHub.

github.com

 

반응형

 

요즘 새 프로젝트를 시작하면서, 이전에 설계했던 방식과 다르게 운영 시스템을 만들게 되었다.

참고로, 내가 일하고 있는 분야(Core Network 시스템, IP Network 시스템)는 개발 방식이 20년 전이나 지금이나 크게 변한게 없다.

그래서 기왕에 새로 개발하는 거라면, 요즘 API 트렌드를 공부하고 설계하는 것이 좋겠다는 생각이 들어서 서점에 가서 최근 2년 사이에 출간한 IT 도서를 몇권 구입해서 왔다.

그 중에서 Arnaud Lauret이 쓴 The Design of Web APIs를 읽어보니 API 설계를 쉽게 설명해줘서 재미있게 읽었다.

 

회사에서 API 설계할 일이 있어서, 그 동안 통밥으로 API를 설계/구현한 일이 몇번 있지만, 내가 잘 알고 한 일이 아니라서 항상 협업하는 동료한테 미안한 마음이 있었다.

 

책을 읽고 좀더 API 설계에 대한 기준이 세워진 것 같아서 좋았다.

 

그리고 책 중간 중간에 유용한 도구를 소개해주고 있는데, API 관련 개발 도구 목록도 있어서 메모를 해본다.

 

 

https://OpenAPI.tools

 

 

 

 

 

 

OpenAPI Generator

 

OAS (OpenAPI Specification) 문서를 이용해서 OpenAPI Source Code를 프로그래밍 언어별로 생성해주는 Tool

 

https://openapi-generator.tech/docs/generators/go

 

OpenAPI Generator · Generate clients, servers, and documentation from OpenAPI 2.0/3.x documents

## METADATA

openapi-generator.tech

 

+ Recent posts