Raw Socket (zz)

本文介绍Windows2000环境下使用RawSocket编程的方法,包括创建原始套接字、设置IP头选项、构造IP头和TCP头、发送原始套接字数据报等内容。文中还提供了一个SYN扫描示例程序。
Raw Socket (zz)
2010年01月05日
  Windows2000在TCP/IP协议组件上做了很多改进,功能也有增强。比如在协议栈上的调整,增大了默认窗口大小,以及高延迟链接新算法。同时在安全性上,可应用IPSec加强安全性,比NT下有不少的改进。
  Microsoft TCP/IP 组件包含“核心协议”、“服务”及两者之间的“接口”。传输驱动程序接口 (TDI) 与网络设备接口规范 (NDIS) 是公用的。 此外,还有许多用户模型应用程序的更高级接口。最常用的接口是 Windows Sockets、远程过程调用 (RPC) 和 NetBIOS。
  Windows Sockets 是一个编程接口,它是在加州大学伯克利分校开发的套接字接口的基础上定义的。它包括了一组扩展件,以充分利用 Microsoft Windows 消息驱动的特点。规范的 1.1 版是在 1993 年 1 月发行的,2.2.0 版在 1996 年 5 月发行。Windows 2000 支持 Winsock 2.2 版。在Winsock2中,支持多个传输协议的原始套接字,重叠I/O模型、服务质量控制等。
  这里介绍Windows Sockets的一些关于原始套接字(Raw Socket)的编程。同Winsock1相比,最明显的就是支持了Raw Socket套接字类型,通过原始套接字,我们可以更加自如地控制Windows下的多种协议,而且能够对网络底层的传输机制进行控制。
  1、创建一个原始套接字,并设置IP头选项。
  SOCKET sock;
  sock = socket(AF_INET,SOCK_RAW,IPPROTO_IP);
  或者:
  s = WSASoccket(AF_INET,SOCK_RAW,IPPROTO_IP,NULL,0,WSA_FLAG_OVERLAPPED);
  这里,我们设置了SOCK_RAW标志,表示我们声明的是一个原始套接字类型。创建原始套接字后,IP头就会包含在接收的数据中,如果我们设定 IP_HDRINCL 选项,那么,就需要自己来构造IP头。注意,如果设置IP_HDRINCL 选项,那么必须具有 administrator权限,要不就必须修改注册表:
  HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Afd\Parameter\
  修改键:DisableRawSecurity(类型为DWORD),把值修改为 1。如果没有,就添加。
  BOOL blnFlag=TRUE;
  setsockopt(sock, IPPROTO_IP, IP_HDRINCL, (char *)&blnFlag, sizeof(blnFlag);
  对于原始套接字在接收数据报的时候,要注意这么几点:
  1、如果接收的数据报中协议类型和定义的原始套接字匹配,那么,接收的所有数据就拷贝到套接字中。
  2、如果绑定了本地地址,那么只有接收数据IP头中对应的远端地址匹配,接收的数据就拷贝到套接字中。
  3、如果定义的是外部地址,比如使用connect(),那么,只有接收数据IP头中对应的源地址匹配,接收的数据就拷贝到套接字中。
  2、构造IP头和TCP头
  这里,提供IP头和TCP头的结构:
  // Standard TCP flags
  #define URG 0x20
  #define ACK 0x10
  #define PSH 0x08
  #define RST 0x04
  #define SYN 0x02
  #define FIN 0x01
  typedef struct _iphdr //定义IP首部
  {
  unsigned char h_lenver; //4位首部长度+4位IP版本号
  unsigned char tos; //8位服务类型TOS
  unsigned short total_len; //16位总长度(字节)
  unsigned short ident; //16位标识
  unsigned short frag_and_flags; //3位标志位
  unsigned char ttl; //8位生存时间 TTL
  unsigned char proto; //8位协议 (TCP, UDP 或其他)
  unsigned short checksum; //16位IP首部校验和
  unsigned int sourceIP; //32位源IP地址
  unsigned int destIP; //32位目的IP地址
  }IP_HEADER;
  typedef struct psd_hdr //定义TCP伪首部
  {
  unsigned long saddr; //源地址
  unsigned long daddr; //目的地址
  char mbz;
  char ptcl; //协议类型
  unsigned short tcpl; //TCP长度
  }PSD_HEADER;
  typedef struct _tcphdr //定义TCP首部
  {
  USHORT th_sport; //16位源端口
  USHORT th_dport; //16位目的端口
  unsigned int th_seq; //32位序列号
  unsigned int th_ack; //32位确认号
  unsigned char th_lenres; //4位首部长度/6位保留字
  unsigned char th_flag; //6位标志位
  USHORT th_win; //16位窗口大小
  USHORT th_sum; //16位校验和
  USHORT th_urp; //16位紧急数据偏移量
  }TCP_HEADER;
  TCP伪首部并不是真正存在的,只是用于计算检验和。校验和函数:
  USHORT checksum(USHORT *buffer, int size)
  {
  unsigned long cksum=0;
  while (size > 1)
  {
  cksum += *buffer++;
  size -= sizeof(USHORT);
  }
  if (size)
  {
  cksum += *(UCHAR*)buffer;
  }
  cksum = (cksum >> 16) + (cksum & 0xffff);
  cksum += (cksum >>16);
  return (USHORT)(~cksum);
  }
  当需要自己填充IP头部和TCP头部的时候,就同时需要自己计算他们的检验和。
  3、发送原始套接字数据报
  填充这些头部稍微麻烦点,发送就相对简单多了。只需要使用sendto()就OK。
  sendto(sock, (char*)&tcpHeader, sizeof(tcpHeader), 0, (sockaddr*)&addr_in,sizeof(addr_in));
  下面是一个示例程序,可以作为SYN扫描的一部分。
  #include
  #include
  #include
  #define SOURCE_PORT 7234
  #define MAX_RECEIVEBYTE 255
  typedef struct ip_hdr //定义IP首部
  {
  unsigned char h_verlen; //4位首部长度,4位IP版本号
  unsigned char tos; //8位服务类型TOS
  unsigned short total_len; //16位总长度(字节)
  unsigned short ident; //16位标识
  unsigned short frag_and_flags; //3位标志位
  unsigned char ttl; //8位生存时间 TTL
  unsigned char proto; //8位协议 (TCP, UDP 或其他)
  unsigned short checksum; //16位IP首部校验和
  unsigned int sourceIP; //32位源IP地址
  unsigned int destIP; //32位目的IP地址
  }IPHEADER;
  typedef struct tsd_hdr //定义TCP伪首部
  {
  unsigned long saddr; //源地址
  unsigned long daddr; //目的地址
  char mbz;
  char ptcl; //协议类型
  unsigned short tcpl; //TCP长度
  }PSDHEADER;
  typedef struct tcp_hdr //定义TCP首部
  {
  USHORT th_sport; //16位源端口
  USHORT th_dport; //16位目的端口
  unsigned int th_seq; //32位序列号
  unsigned int th_ack; //32位确认号
  unsigned char th_lenres; //4位首部长度/6位保留字
  unsigned char th_flag; //6位标志位
  USHORT th_win; //16位窗口大小
  USHORT th_sum; //16位校验和
  USHORT th_urp; //16位紧急数据偏移量
  }TCPHEADER;
  //CheckSum:计算校验和的子函数
  USHORT checksum(USHORT *buffer, int size)
  {
  unsigned long cksum=0;
  while(size >1)
  {
  cksum+=*buffer++;
  size -=sizeof(USHORT);
  }
  if(size )
  {
  cksum += *(UCHAR*)buffer;
  }
  cksum = (cksum >> 16) + (cksum & 0xffff);
  cksum += (cksum >>16);
  return (USHORT)(~cksum);
  }
  void useage()
  {
  printf("******************************************\n");
  printf("TCPPing\n");
  printf("\t Written by Refdom\n");
  printf("\t Email: refdom@263.net\n");
  printf("Useage: TCPPing.exe Target_ip Target_port \n");
  printf("*******************************************\n");
  }
  int main(int argc, char* argv[])
  {
  WSADATA WSAData;
  SOCKET sock;
  SOCKADDR_IN addr_in;
  IPHEADER ipHeader;
  TCPHEADER tcpHeader;
  PSDHEADER psdHeader;
  char szSendBuf[60]={0};
  BOOL flag;
  int rect,nTimeOver;
  useage();
  if (argc!= 3)
  { return false; }
  if (WSAStartup(MAKEWORD(2,2), &WSAData)!=0)
  {
  printf("WSAStartup Error!\n");
  return false;
  }
  if ((sock=WSASocket(AF_INET,SOCK_RAW,IPPROTO_RAW,NULL,0,WSA_FLAG_OVERLAPPED))==INVALID_SOCKET)
  {
  printf("Socket Setup Error!\n");
  return false;
  }
  flag=true;
  if (setsockopt(sock,IPPROTO_IP, IP_HDRINCL,(char *)&flag,sizeof(flag))==SOCKET_ERROR)
  {
  printf("setsockopt IP_HDRINCL error!\n");
  return false;
  }
  nTimeOver=1000;
  if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (char*)&nTimeOver, sizeof(nTimeOver))==SOCKET_ERROR)
  {
  printf("setsockopt SO_SNDTIMEO error!\n");
  return false;
  }
  addr_in.sin_family=AF_INET;
  addr_in.sin_port=htons(atoi(argv[2]));
  addr_in.sin_addr.S_un.S_addr=inet_addr(argv[1]);
  //
  //
  //填充IP首部
  ipHeader.h_verlen=(4socket上的数据,这是因为,所有的IP包都是先递交给系统核心,然后再传输到用户程序,当发送一个raws socket包的时候(比如syn),核心并不知道,也没有这个数据被发送或者连接建立的记录,因此,当远端主机回应的时候,系统核心就把这些包都全部丢掉,从而到不了应用程序上。所以,就不能简单地使用接收函数来接收这些数据报。
  要达到接收数据的目的,就必须采用嗅探,接收所有通过的数据包,然后进行筛选,留下符合我们需要的。可以再定义一个原始套接字,用来完成接收数据的任务,需要设置SIO_RCVALL,表示接收所有的数据。
  SOCKET sniffersock;
  sniffsock = WSASocket(AF_INET, SOCK_RAW, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED);
  DWORD lpvBuffer = 1;
  DWORD lpcbBytesReturned = 0 ;
  WSAIoctl(sniffersock, SIO_RCVALL, &lpvBuffer, sizeof(lpvBuffer), NULL, 0, & lpcbBytesReturned, NULL, NULL);
  创建一个用于接收数据的原始套接字,我们可以用接收函数来接收数据包了。然后在使用一个过滤函数达到筛选的目的,接收我们需要的数据包。
  如果在XP以上的操作系统,微软封杀了Raw Soccket,只能用wincpap之类的开发包了。
guyue@k8s-worker01:~$ # 停止所有服务 sudo systemctl stop kubelet sudo systemctl stop cri-docker.service sudo systemctl stop containerd sudo systemctl stop docker # 彻底清理 kubeadm 配置 sudo kubeadm reset --force --cri-socket unix:///var/run/cri-dockerd.sock # 手动删除所有残留配置 sudo rm -rf /etc/kubernetes/* sudo rm -rf /var/lib/kubelet/* sudo rm -rf /var/lib/etcd/* sudo rm -rf /etc/cni/net.d/* # 清理 iptables 规则 sudo iptables -F sudo iptables -t nat -F sudo iptables -t mangle -F sudo iptables -t raw -F sudo systemctl daemon-reload default | xargs -r sudo ip netns delete Stopping 'docker.service', but its triggering units are still active: docker.socket [preflight] Running pre-flight checks W1202 13:26:55.625312 40333 removeetcdmember.go:106] [reset] No kubeadm config, using etcd pod spec to get data directory [reset] Deleted contents of the etcd data directory: /var/lib/etcd [reset] Stopping the kubelet service [reset] Unmounting mounted directories in "/var/lib/kubelet" W1202 13:26:55.768797 40333 cleanupnode.go:99] [reset] Failed to remove containers: output: E1202 13:26:55.763484 40339 remote_runtime.go:277] "ListPodSandbox with filter from runtime service failed" err="rpc error: code = Unavailable desc = connection error: desc = \"transport: Error while dialing dial unix /var/run/cri-dockerd.sock: connect: connection refused\"" filter="&PodSandboxFilter{Id:,State:nil,LabelSelector:map[string]string{},}" time="2025-12-02T13:26:55+08:00" level=fatal msg="listing pod sandboxes: rpc error: code = Unavailable desc = connection error: desc = \"transport: Error while dialing dial unix /var/run/cri-dockerd.sock: connect: connection refused\"" , error: exit status 1 [reset] Deleting contents of directories: [/etc/kubernetes/manifests /var/lib/kubelet /etc/kubernetes/pki] [reset] Deleting files: [/etc/kubernetes/admin.conf /etc/kubernetes/kubelet.conf /etc/kubernetes/bootstrap-kubelet.conf /etc/kubernetes/controller-manager.conf /etc/kubernetes/scheduler.conf] The reset process does not clean CNI configuration. To do so, you must remove /etc/cni/net.d The reset process does not reset or clean up iptables rules or IPVS tables. If you wish to reset iptables, you must do so manually by using the "iptables" command. If your cluster was setup to utilize IPVS, run ipvsadm --clear (or similar) to reset your system's IPVS tables. The reset process does not clean your kubeconfig files and you must remove them manually. Please, check the contents of the $HOME/.kube/config file. guyue@k8s-worker01:~$ # 创建专用的 kubelet 配置目录 sudo mkdir -p /etc/systemd/system/kubelet.service.d/ # 创建正确的配置文件(使用 .conf 后缀) sudo tee /etc/systemd/system/kubelet.service.d/10-kubeadm.conf <<EOF [Service] Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf" Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml" Environment="KUBELET_EXTRA_ARGS=--container-runtime-endpoint=unix:///var/run/cri-dockerd.sock --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.9 --v=2" EOF # 重新加载 systemd 配置 sudo systemctl daemon-reload # 验证配置 sudo systemctl cat kubelet [Service] Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf" Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml" Environment="KUBELET_EXTRA_ARGS=--container-runtime-endpoint=unix:///var/run/cri-dockerd.sock --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.9 --v=2" # /usr/lib/systemd/system/kubelet.service [Unit] Description=kubelet: The Kubernetes Node Agent Documentation=https://kubernetes.io/docs/home/ Wants=network-online.target After=network-online.target [Service] ExecStart=/usr/bin/kubelet Restart=always StartLimitInterval=0 RestartSec=10 [Install] WantedBy=multi-user.target # /etc/systemd/system/kubelet.service.d/0-containerd.conf [Service] Environment="KUBELET_EXTRA_ARGS=--container-runtime-endpoint=unix:///var/run/cri> lines 1-20 [4]+ 已停止 sudo systemctl cat kubelet guyue@k8s-worker01:~$ # 创建或覆盖 /etc/default/kubelet(最高优先级配置) echo 'KUBELET_EXTRA_ARGS="--container-runtime-endpoint=unix:///var/run/cri-dockerd.sock --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.9"' | sudo tee /etc/default/kubelet # 启动 cri-dockerd sudo systemctl start cri-docker.service sudo systemctl enable cri-docker.service # 验证 CRI 连接 sudo crictl --runtime-endpoint unix:///var/run/cri-dockerd.sock version sudo crictl --runtime-endpoint unix:///var/run/cri-dockerd.sock info KUBELET_EXTRA_ARGS="--container-runtime-endpoint=unix:///var/run/cri-dockerd.sock --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.9" Version: 0.1.0 RuntimeName: docker RuntimeVersion: 29.0.0 RuntimeApiVersion: v1 { "status": { "conditions": [ { "type": "RuntimeReady", "status": true, "reason": "", "message": "" }, { "type": "NetworkReady", "status": false, "reason": "NetworkPluginNotReady", "message": "docker: network plugin is not ready: cni config uninitialized" } ] }, "config": { "sandboxImage": "registry.aliyuncs.com/google_containers/pause:3.10.1" } } guyue@k8s-worker01:~$ # 启动 kubelet sudo systemctl start kubelet sudo systemctl enable kubelet # 监控日志(在新终端中运行) sudo journalctl -u kubelet -f --since "now" 12月 02 13:27:38 k8s-worker01 kubelet[40971]: I1202 13:27:38.069109 40971 server.go:467] "Kubelet version" kubeletVersion="v1.28.2" 12月 02 13:27:38 k8s-worker01 kubelet[40971]: I1202 13:27:38.069243 40971 server.go:469] "Golang settings" GOGC="" GOMAXPROCS="" GOTRACEBACK="" 12月 02 13:27:38 k8s-worker01 kubelet[40971]: I1202 13:27:38.069532 40971 server.go:630] "Standalone mode, no API client" 12月 02 13:27:38 k8s-worker01 kubelet[40971]: E1202 13:27:38.073889 40971 run.go:74] "command failed" err="failed to run Kubelet: validate service connection: validate CRI v1 runtime API for endpoint \"unix:///run/containerd/containerd.sock\": rpc error: code = Unimplemented desc = unknown service runtime.v1.RuntimeService" 12月 02 13:27:38 k8s-worker01 systemd[1]: kubelet.service: Main process exited, code=exited, status=1/FAILURE 12月 02 13:27:38 k8s-worker01 systemd[1]: kubelet.service: Failed with result 'exit-code'. 12月 02 13:27:38 k8s-worker01 systemd[1]: kubelet.service: Consumed 1.915s CPU time, 7.0M memory peak, 0B memory swap peak. 12月 02 13:27:48 k8s-worker01 systemd[1]: kubelet.service: Scheduled restart job, restart counter is at 42. 12月 02 13:27:48 k8s-worker01 systemd[1]: Started kubelet.service - kubelet: The Kubernetes Node Agent. 12月 02 13:27:48 k8s-worker01 kubelet[41091]: I1202 13:27:48.478732 41091 server.go:467] "Kubelet version" kubeletVersion="v1.28.2" 12月 02 13:27:48 k8s-worker01 kubelet[41091]: I1202 13:27:48.478879 41091 server.go:469] "Golang settings" GOGC="" GOMAXPROCS="" GOTRACEBACK="" 12月 02 13:27:48 k8s-worker01 kubelet[41091]: I1202 13:27:48.479651 41091 server.go:630] "Standalone mode, no API client" 12月 02 13:27:48 k8s-worker01 kubelet[41091]: E1202 13:27:48.485767 41091 run.go:74] "command failed" err="failed to run Kubelet: validate service connection: validate CRI v1 runtime API for endpoint \"unix:///run/containerd/containerd.sock\": rpc error: code = Unimplemented desc = unknown service runtime.v1.RuntimeService" 12月 02 13:27:48 k8s-worker01 systemd[1]: kubelet.service: Main process exited, code=exited, status=1/FAILURE 12月 02 13:27:48 k8s-worker01 systemd[1]: kubelet.service: Failed with result 'exit-code'. ^Z [5]+ 已停止 sudo journalctl -u kubelet -f --since "now" guyue@k8s-worker01:~$ # 在确认 kubelet 正常运行后(看到类似 "Starting to listen on 0.0.0.0:10250" 的日志) sudo kubeadm join 192.168.130.129:6443 \ --token h0epjb.94zz1xhekvf7nfzx \ --discovery-token-ca-cert-hash sha256:a7760aa4d05b6bf0af6be91267c86417999c998318bfe4a8afcb62b757b8f808 \ --cri-socket unix:///var/run/cri-dockerd.sock \ --ignore-preflight-errors=all [preflight] Running pre-flight checks [preflight] Reading configuration from the cluster... [preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml' [kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml" [kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env" [kubelet-start] Starting the kubelet [kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap... [kubelet-check] Initial timeout of 40s passed. [kubelet-check] It seems like the kubelet isn't running or healthy. [kubelet-check] The HTTP call equal to 'curl -sSL http://localhost:10248/healthz' failed with error: Get "http://localhost:10248/healthz": dial tcp 127.0.0.1:10248: connect: connection refused. [kubelet-check] It seems like the kubelet isn't running or healthy. [kubelet-check] The HTTP call equal to 'curl -sSL http://localhost:10248/healthz' failed with error: Get "http://localhost:10248/healthz": dial tcp 127.0.0.1:10248: connect: connection refused. [kubelet-check] It seems like the kubelet isn't running or healthy. [kubelet-check] The HTTP call equal to 'curl -sSL http://localhost:10248/healthz' failed with error: Get "http://localhost:10248/healthz": dial tcp 127.0.0.1:10248: connect: connection refused.
最新发布
12-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值