udp socket学习——实现SNMP代理

udp socket学习——实现SNMP代理

欢迎大家给我提意见~

一.实验目标

1.基于c语言使用UDP socket技术实现SNMP网关的基本功能,客户端能和服务端建立联系,客户端发送get/set信息到程序,程序识别客户端信息,把相应信息发给服务端,服务端收到后返回客户端,实现双向收发。
2. 程序能在Windows系统和Linux系统下编译运行。

二.概要

(一)SNMP介绍

SNMP(Simple Network Management Protocol)简单网络管理协议的基本网络架构如下:

1.结构介绍:

NMS是采用 SNMP 协议对网络设备进行管理/监视的系统,运行在 NMS 服务器上。
agent是被管理设备中的一个代理进程,用于维护被管理设备的信息数据并响应来自 NMS 的请求。
MIB为被管理对象的集合,每个SNMP设备(Agent)都有自己的MIB ,Agent 接收到 NMS 的请求信息后,通过 MIB 表完成相应指令后,并把操作结果响应给 NMS。MIB以树状结构进行存储,树的叶子节点表示管理对象,它可以通过从根节点开始的一条惟一路径来识别,这也就是OID(Object Identifier)。

2.SNMP处理过程:

客户端通过UDP端口给代理端发送报文信息,在服务端设备上的Agent通过UDP端口161接收串行化报文,通过解码、验证团体名、分析出MIB中对应的节点,从中获得管理变量的值,形成响应的报文,回发给网络管理站,网络管理站接收后经过相同的处理报文操作,显示结果。
客户端可以发送两种操作命令:Get-Request可以让管理站读取代理者处对象的值,从设备中获取信息;Set-Request可以让管理站设置代理者处对象的值,可以通过它来改动设备的配置或控制设备的运转状态(例如设置设备的名称,关掉端口或清除地址解析表中的项等)。
服务端可以发送两种命令:get-Response相应客户端信息;Trap向管理站发送信息(非请求),描述事件,通过UDP端口162发送命令

3.SNMP报文结构:

版本识别符:确保SNMP代理使用相同的协议,SNMP代理会直接抛弃与自己协议版本不同的数据报,写入版本字段的是版本号减1。
团体名:基本的安全机制,管理员访问SNMP管理代理时的身份验证,默认值为 public。如果把配置管理代理成可以执行Trap命令,当网络管理 员用一个错误的分区名查询管理代理时,系统就发送一个trap报文。
SNMP管理站用Get-Request消息从拥有SNMP代理的网络设备中检索信息,而SNMP代理则用Get-Response消息响应。Get-Next-Request用于和Get-Request组合起来查询特定的表对象中的列元素。
4.UDP传输协议
UDP是一个简单的面向数据报无连接的运输层协议:进程的每个输出操作都正好产生一个UDP数据报,并组装成一份待发送的IP数据报。
数据报封装格式如下:

(二)网关介绍

网关是一种充当转换重任的计算机系统或设备。在使用不同的通信协议、数据格式或语言,甚至体系结构完全不同的两种系统之间,网关是一个翻译器。与网桥只是简单地传达信息不同,网关对收到的信息要重新打包,以适应目的系统的需求。同时,网关也可以提供过滤和安全功能。
传输网关用于在2个网络间建立传输连接。利用传输网关,不同网络上的主机间可以建立起跨越多个网络的、级联的、点对点的传输连接。例如通常使用的路由器就是传输网关,“网关”的作用体现在连接两个不同的网段,或者是两个不同的路由协议之间的连接。

Socket介绍

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面。
实例建立:SOCKET socket(int af, int type, int protocol)
domain: 协议族;IPv4为AF_INET,IPv6为AF_INET6
type:socket的传输⽅式;TCP为SOCK_STREAM,UDP为SOCK_DGRAM
protocol:指定socket使⽤的协议,IPPROTO_TCP(TCP 传输协议),IPPROTO_UDP(UDP 传输协议)
套接字函数:
int bind(int sockfd, (SOCKET sock,const struct sockeraddr *addr, int addrlen)
addr:绑定的地址
addrlen:addr结构的⼤⼩
SOCKET accept(SOCKET sock, struct sockaddr *addr,int *addrlen);
addr:sockaddr_in 结构体变量;
addrlen: addr 的长度;
int send(SOCKET sock, const char *buf, int len, int flags);
sock:要发送数据的套接字对象;
buf:要发送的数据的缓冲区地址;
len:为要发送的数据的字节数;
flags:为发送数据时的选项,参数值一般设置为 0 或 NULL;
int recv(SOCKET sock, const char *buf, int len, int flags);
sock:要接收数据的套接字对象;
buf:要接收的数据的缓冲区地址;
len:为要接收的数据的字节数;
flags:为接收数据时的选项,参数值一般设置为 0 或 NULL;
结构体 struct sockaddr_in{
sa_family_t sin_family; //地址族(Address Family),也就是地址类型
uint16_t sin_port; //16 位的端口号
struct in_addr sin_addr; //32 位 IP 地址
char sin_zero[8]; //不使用,一般用 0 填充
};

(二)UDP socket编写程序代替SNMP中网关作用,总体设计如下:

客户端发送snmp报文,发送端口为随机端口,发送地址为客户端地址;发送到连接端口,发送的目的地址为程序运行地址。程序在连接端口绑定socket进行监听,收到报文后,从监听端口发往udp端口161,发送地址为程序运行地址,目的地址为服务端地址。程序接收来自服务端端口161的回送报文,目的端口为连接端口,地址为程序运行的地址。从连接端口发往客户端,发送端口为连接端口,地址为程序地址,目的端口为客户端发送时的连接端口,目的地址为客户端地址。
udp实现snmp代理
三.详细方案
(一)使用UDP STOCKET实现SNMP网关功能
开发环境:Visual Studio 2022 c语言环境
运行环境:Windows 10,Linux
程序设计:

创建套接字:SOCK_DGRAM

四.测试分析

(一)本机MIB和程序连接
运行环境:Windows 10
服务端程序环境:Visual Studio 2022
客户端:MIB Brower
运行MIB Brower,设置连接地址为本机地址127.0.0.1,开始连接:
在这里插入图片描述
连接成功,程序收发信息:

在这里插入图片描述

MIB Brower显示连接情况:

进行get操作,成功获取信息:
在这里插入图片描述

(二)本机MIB和Linux服务器程序连接测试:
服务端运行环境:Linux
编译:gcc
Linux连接工具:Xshell 7
客户端:MIB Brower,运行在Windows环境
成功与Linux建立连接:
启动Linux程序udp和mib brower,成功连接:

抓包查看:
在这里插入图片描述

源码:`

#include <stdio.h>
#include <string.h>
//#define WIN32//在win上运行时取消注释
#define SPORT 8081
#define SEVERADDR xxx,xxx,xxx,xxx//填写snmp服务器地址

#ifdef WIN32  //在Windows下编译的头文件
      #include <winsock2.h>
      #include<Windows.h>
      #include<WS2tcpip.h>

      #pragma comment(lib,"ws2_32.lib")

#else        //在Linux下编译的头文件
  #include <stdlib.h>
  #include <unistd.h>
  #include <sys/types.h>
  #include <sys/socket.h>
  #include <arpa/inet.h>
  #include <netinet/in.h>
  #include <assert.h>
  #include<errno.h>
  #define SOCKET int
  #define closesocket close
#endif
int InitSocket()
   {
    #ifdef WIN32
	   WORD sockVision = MAKEWORD(2, 2);
	   WSADATA wsadata;

	   if (WSAStartup(sockVision, &wsadata) != 0)
	   {
		   printf("WSA初始化失败\n");
		   return 0;
	    }
	   return 1;
   #else
        return 1;
   #endif
   }
void FreeSock()//释放socket
{
#ifdef WIN32
     WSACleanup();

#endif
}

int main(void)
{
	//其他变量
	
	SOCKET serSocket;

	struct sockaddr_in clientAddr={0};//发送方的地址信息
	struct sockaddr_in recvAddr;          //获得的地址信息
	struct sockaddr_in serverAddr;       //接收方的地址信息
	struct sockaddr_in sin;
	unsigned char aucServerAddr[4] = {SEVERADDR};
	int ret = -1;                        //
	socklen_t nAddrlen = (socklen_t)sizeof(clientAddr);   //发送方地址的长度
	char revdata[1600]={0};                  //暂存区

	unsigned short usPort = 0;                     //链接端口
	int flag = 1;

    if(!InitSocket())
	{
          return -1;
         }
	///创建套接字
	serSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (serSocket<0)
	{
	     printf("socket服务器创建失败\n");
	     return -1;
			}
	
	serverAddr.sin_family = AF_INET;  //使用IPv4
	memcpy(&serverAddr.sin_addr.s_addr, aucServerAddr, 4);//复制存储的地址到socket中
	//serverAddr.sin_addr.s_addr= inet_addr("xxx");
	serverAddr.sin_port = htons(161);

	//绑定连接的IP和端口
	sin.sin_family = AF_INET;
	sin.sin_port = htons(SPORT);
	#ifdef WIN32
	   sin.sin_addr.S_un.S_addr = INADDR_ANY;
	#else
       sin.sin_addr.s_addr=INADDR_ANY;//inet_addr("xxx");htonl()
	#endif
	if (bind(serSocket, (struct sockaddr*)&sin, sizeof(sin)) !=0)
	{
		printf("绑定IP和端口\n");
		return 0;
	}
    else
    {
          printf("已连接\n");
    }
	//循环接收数据
	while (flag)
	{
	  memset(revdata, 0, sizeof(revdata));
	  ret = recvfrom(serSocket, revdata,1600, 0,(struct sockaddr * )&recvAddr, &nAddrlen);
//打印信息
		fd_set udpSetAnni;
	    FD_ZERO(&udpSetAnni);
        FD_SET(serSocket, &udpSetAnni);
	    char client_ip[INET_ADDRSTRLEN];
	    inet_ntop(AF_INET, &(recvAddr.sin_addr), client_ip, INET_ADDRSTRLEN);
	    printf("IP: %s, Port: %u\n", client_ip, ntohs(recvAddr.sin_port));
	    //end
	if (ret <= 0)
		{
			continue;
		}
	 else
		{
		printf("已接收:%d\n",ret);
		}
		usPort = ntohs(recvAddr.sin_port);//记录接收信息的端口
		printf("接收:%u\n",usPort);
	if (usPort == 161) // msg from server
	{   
			
			sendto(serSocket, revdata, ret, 0, (struct sockaddr*)&clientAddr, nAddrlen);
			printf("send information \n");
			printf("\n");
	}
	else
	{
		    
			memcpy(&clientAddr, &recvAddr, sizeof(clientAddr));//复制接收方地址信息
			int rv=sendto(serSocket, revdata, ret, 0, (struct sockaddr*)&serverAddr, nAddrlen);
			if(rv < 0)
			{
				printf("reply data failure %s\n",strerror(errno));
				continue;
			}
			else
				{printf("send msg to 161\n");}

	}
		
	}
	
            closesocket(serSocket);
            FreeSock();
	return 0;
}

`

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值