目录

前言

1 arp数据包文接收 arp_rcv()

1.1 处理arp请求 arp_process()【核心】

2 arp数据包发送 arp_send()

2.1 arp 数据包构造 arp_create()

2.2 arp_send() 发送场景

3 arp 邻居项的创建 + arp solicit 请求发送流程

3.1 创建+发送流程

3.2 arp请求报文发送 arp_solicit()


前言

在arp初始化时,通过调用dev_add_pack将arp协议的接收处理函数添加到了三层协议数据包处理函数相关的hash链表ptype_base中(关于三层协议数据包处理函数相关的hash链表,参见《linux内核协议栈 三 / 四层协议接收数据处理函数以及相关的全局 hash表 / 数组》)。当底层接收到属于本机的arp数据包时,就会调用arp_rcv进行后续处理。

1 arp数据包文接收 arp_rcv()

功能:对接收到的arp数据包的处理函数

  1. 首先对arp数据包进行合理性检查。
  2. 调用NF_HOOK,判断是否需要对arp进行进一步的处理,对于需要进一步处理的数据包,则调用 arp_process() 进行后续处理。
/**  Receive an arp request from the device layer.*/static int arp_rcv(struct sk_buff *skb, struct net_device *dev,struct packet_type *pt, struct net_device *orig_dev)
{const struct arphdr *arp;if (dev->flags & IFF_NOARP ||skb->pkt_type == PACKET_OTHERHOST ||skb->pkt_type == PACKET_LOOPBACK)goto freeskb;skb = skb_share_check(skb, GFP_ATOMIC);if (!skb)goto out_of_mem;/* ARP header, plus 2 device addresses, plus 2 IP addresses.  */if (!pskb_may_pull(skb, arp_hdr_len(dev)))goto freeskb;arp = arp_hdr(skb);if (arp->ar_hln != dev->addr_len || arp->ar_pln != 4)goto freeskb;memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));return NF_HOOK(NFPROTO_ARP, NF_ARP_IN, skb, dev, NULL, arp_process);freeskb:kfree_skb(skb);
out_of_mem:return 0;
}

1.1 处理arp请求 arp_process()【核心】

arp_process() 用于处理一个arp请求,主要是考虑如下几个方面:

  1. arp数据包的格式是否正确,是否是属于系统支持的邻居项协议
  2. 是否需要丢弃接收到的arp数据包
  3. 处理符合条件的arp数据包。

下面是处理arp包的几个条件:

丢弃数据包的标准:

  1. arp_process只处理request、reply的arp数据包,丢弃其他类型的数据包
  2. 对于类型为request的数据包,丢弃目的地址是组播或者loopback的arp数据。

对于需要处理的数据包,大致可以分为几个方面:

1、对本机发送的arp 请求的应答数据包的处理

2、arp 请求数据包

a)目的地址是本地地址,且源地址不为0的arp 请求数据包

b)目的地址是本地地址,且源地址为0的重复地址检测的arp请求数据包

3、非本地发送的arp请求的应答数据包,执行arp代理,具体原理以及过程参见《ARP代理(善意的欺骗)》

/**  Process an arp request.*/static int arp_process(struct sk_buff *skb)
{struct net_device *dev = skb->dev;struct in_device *in_dev = __in_dev_get_rcu(dev);struct arphdr *arp;unsigned char *arp_ptr;struct rtable *rt;unsigned char *sha;__be32 sip, tip;u16 dev_type = dev->type;int addr_type;struct neighbour *n;struct net *net = dev_net(dev);/* arp_rcv below verifies the ARP header and verifies the device* is ARP'able.*/if (in_dev == NULL)goto out;//调用arp_hdr获取skb数据中arp头的开始指针arp = arp_hdr(skb);//判断设备的类型与数据包中的硬件类型是否相符switch (dev_type) {default:if (arp->ar_pro != htons(ETH_P_IP) ||htons(dev_type) != arp->ar_hrd)goto out;break;case ARPHRD_ETHER:case ARPHRD_FDDI:case ARPHRD_IEEE802:/** ETHERNET, and Fibre Channel (which are IEEE 802* devices, according to RFC 2625) devices will accept ARP* hardware types of either 1 (Ethernet) or 6 (IEEE 802.2).* This is the case also of FDDI, where the RFC 1390 says that* FDDI devices should accept ARP hardware of (1) Ethernet,* however, to be more robust, we'll accept both 1 (Ethernet)* or 6 (IEEE 802.2)*/if ((arp->ar_hrd != htons(ARPHRD_ETHER) &&arp->ar_hrd != htons(ARPHRD_IEEE802)) ||arp->ar_pro != htons(ETH_P_IP))goto out;break;case ARPHRD_AX25:if (arp->ar_pro != htons(AX25_P_IP) ||arp->ar_hrd != htons(ARPHRD_AX25))goto out;break;case ARPHRD_NETROM:if (arp->ar_pro != htons(AX25_P_IP) ||arp->ar_hrd != htons(ARPHRD_NETROM))goto out;break;}/* Understand only these message types */if (arp->ar_op != htons(ARPOP_REPLY) &&arp->ar_op != htons(ARPOP_REQUEST))goto out;/**   Extract fields*///获取arp数据包中源mac地址、源ip地址、目的mac地址、目的ip地址arp_ptr = (unsigned char *)(arp + 1);sha    = arp_ptr;arp_ptr += dev->addr_len;memcpy(&sip, arp_ptr, 4);arp_ptr += 4;switch (dev_type) {
#if IS_ENABLED(CONFIG_FIREWIRE_NET)case ARPHRD_IEEE1394:break;
#endifdefault:arp_ptr += dev->addr_len;}memcpy(&tip, arp_ptr, 4);
/** Check for bad requests for 127.x.x.x and requests for multicast*    addresses.  If this is one such, delete it.*///丢弃目的地址是组播或者loopback的arp数据(对于组播地址和loopback地址是不需要arp)if (ipv4_is_multicast(tip) ||(!IN_DEV_ROUTE_LOCALNET(in_dev) && ipv4_is_loopback(tip)))goto out;/**     Special case: We must set Frame Relay source Q.922 address*/if (dev_type == ARPHRD_DLCI)sha = dev->broadcast;/**  Process entry.  The idea here is we want to send a reply if it is a*  request for us or if it is a request for someone else that we hold*  a proxy for.  We want to add an entry to our cache if it is a reply*  to us or if it is a request for our address.*  (The assumption for this last is that if someone is requesting our*  address, they are probably intending to talk to us, so it saves time*  if we cache their address.  Their address is also probably not in*  our cache, since ours is not in their cache.)**  Putting this another way, we only care about replies if they are to*  us, in which case we add them to the cache.  For requests, we care*  about those for us and those for our proxies.  We reply to both,*  and in the case of requests for us we add the requester to the arp*  cache.*//* Special case: IPv4 duplicate address detection packet (RFC2131) *//*对于源ip地址是0的arp请求,一般用于重复地址检测,./arping -U x.x.x.x此时如果 arp 类型为request,且目的ip地址是本地地址,且可以进行arp应答时,则调用arp_send发送arp reply数据包。问题:对于源地址为0的数据包,在发送arp 应答报文时,为什么没有先查找路由表呢?答:在我们建立路由表时,都会建立一个全零的默认路由,所以对于目的ip为0的数据包,其路由是一直存在的。所以在处理时不用查找路由表,直接生成arp reply数据包,并发送出去。[root@xxxx ~]# route -nKernel IP routing tableDestination     Gateway         Genmask         Flags Metric Ref    Use Iface0.0.0.0         10.x.x.x        0.0.0.0         UG    0      0        0 eth0*/if (sip == 0) {if (arp->ar_op == htons(ARPOP_REQUEST) &&inet_addr_type(net, tip) == RTN_LOCAL &&!arp_ignore(in_dev, sip, tip))arp_send(ARPOP_REPLY, ETH_P_ARP, sip, dev, tip, sha,dev->dev_addr, sha);goto out;}//对于arp 类型为request的数据包,且能找到到目的地址tip的路由,则执行下面的代码if (arp->ar_op == htons(ARPOP_REQUEST) &&ip_route_input_noref(skb, tip, sip, 0, dev) == 0) {//获取tip对应的路由缓存rt = skb_rtable(skb);addr_type = rt->rt_type;/*1、如果路由缓存对应的ip地址类型为local,则调用neigh_event_ns(被动学习arp处理),查找符合条件的邻居项a)如果找到符合条件的邻居项(找不到则创建一个arp表项),则调用arp_send发送对该arp request包的reply包,并返回b)直接返回。2、如果路由缓存对应的ip地址类型不是local,则进行arp proxy的处理,完成后直接返回*/if (addr_type == RTN_LOCAL) {int dont_send;dont_send = arp_ignore(in_dev, sip, tip);if (!dont_send && IN_DEV_ARPFILTER(in_dev))dont_send = arp_filter(sip, tip, dev);if (!dont_send) {n = neigh_event_ns(&arp_tbl, sha, &sip, dev);if (n) {arp_send(ARPOP_REPLY, ETH_P_ARP, sip,dev, tip, sha, dev->dev_addr,sha);neigh_release(n);}}goto out;} else if (IN_DEV_FORWARD(in_dev)) {//arp代理过程if (addr_type == RTN_UNICAST  &&(arp_fwd_proxy(in_dev, dev, rt) ||arp_fwd_pvlan(in_dev, dev, rt, sip, tip) ||(rt->dst.dev != dev &&pneigh_lookup(&arp_tbl, net, &tip, dev, 0)))) {n = neigh_event_ns(&arp_tbl, sha, &sip, dev);if (n)neigh_release(n);if (NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED ||skb->pkt_type == PACKET_HOST ||in_dev->arp_parms->proxy_delay == 0) {arp_send(ARPOP_REPLY, ETH_P_ARP, sip,dev, tip, sha, dev->dev_addr,sha);} else {pneigh_enqueue(&arp_tbl,in_dev->arp_parms, skb);return 0;}goto out;}}}/* Update our ARP tables *//*1、对于arp reply数据包,进入下面的处理流程2、对于arp request数据包,且没有找到tip ip对应的路由缓存*///调用__neigh_lookup,查找arp_tbl的neighbour hash bucket,查找sip对应的邻居项n = __neigh_lookup(&arp_tbl, &sip, dev, 0);//对于系统允许非arp请求的arp reply,则进行如下处理if (IN_DEV_ARP_ACCEPT(in_dev)) {///proc/sys/net/ipv4/conf/all/arp_accept/* Unsolicited ARP is not accepted by default.It is possible, that this option should be enabled for somedevices (strip is candidate)*//*1、对于非由arp请求的arp reply,且没有相应的neighbour,则强制创建新的neighbour2、对于sip与tip相等的arp request,也强制创建新的neighbour(arping -U x.x.x.x)3、注意这里源ip有要求:对端必须是网关或者直连路由可达*/ if (n == NULL &&(arp->ar_op == htons(ARPOP_REPLY) ||(arp->ar_op == htons(ARPOP_REQUEST) && tip == sip)) &&inet_addr_type(net, sip) == RTN_UNICAST)n = __neigh_lookup(&arp_tbl, &sip, dev, 1);}/*如果查找到符合条件的neighbour,则执行如下代码1、对于发给给本机的arp reply报文,则将邻居项设置为reach状态2、对于发给给本机的arp request报文,则将邻居项状态设置为stale状态最后调用neigh_update,更新neighbour的状态3、参数 sha 用于更新远端mac即下一跳的mac地址*/if (n) {int state = NUD_REACHABLE;int override;/* If several different ARP replies follows back-to-back,use the FIRST one. It is possible, if several proxyagents are active. Taking the first reply preventsarp trashing and chooses the fastest router.*/override = time_after(jiffies, n->updated + n->parms->locktime);/* Broadcast replies and request packetsdo not assert neighbour reachability.*/if (arp->ar_op != htons(ARPOP_REPLY) ||skb->pkt_type != PACKET_HOST)state = NUD_STALE;neigh_update(n, sha, state,override ? NEIGH_UPDATE_F_OVERRIDE : 0);neigh_release(n);}out:consume_skb(skb);return 0;
}

2 arp数据包发送 arp_send()

arp_send() 就是 arp_create() 的封装函数,相比arp_creare(),增加了判断设备是否为NOARP的设备。

/**  Create and send an arp packet.*/
void arp_send(int type, int ptype, __be32 dest_ip,struct net_device *dev, __be32 src_ip,const unsigned char *dest_hw, const unsigned char *src_hw,const unsigned char *target_hw)
{struct sk_buff *skb;/**    No arp on this interface.*/if (dev->flags&IFF_NOARP)return;skb = arp_create(type, ptype, dest_ip, dev, src_ip,dest_hw, src_hw, target_hw);if (skb == NULL)return;arp_xmit(skb);
}
EXPORT_SYMBOL(arp_send);/** Send an arp packet.*/
void arp_xmit(struct sk_buff *skb)
{/* Send it off, maybe filter it using firewalling first.  */NF_HOOK(NFPROTO_ARP, NF_ARP_OUT, skb, NULL, skb->dev, dev_queue_xmit);
}
EXPORT_SYMBOL(arp_xmit);

2.1 arp 数据包构造 arp_create()

该函数主要是申请一个缓存,并根据arp协议的格式,创建一个arp数据包。该函数还是比较简单的。

/**  Create an arp packet. If (dest_hw == NULL), we create a broadcast*    message.*/
struct sk_buff *arp_create(int type, int ptype, __be32 dest_ip,struct net_device *dev, __be32 src_ip,const unsigned char *dest_hw,const unsigned char *src_hw,const unsigned char *target_hw)
{struct sk_buff *skb;struct arphdr *arp;unsigned char *arp_ptr;int hlen = LL_RESERVED_SPACE(dev);int tlen = dev->needed_tailroom;/**   Allocate a buffer*///首先调用alloc_skb,申请缓存空间skb = alloc_skb(arp_hdr_len(dev) + hlen + tlen, GFP_ATOMIC);if (skb == NULL)return NULL;skb_reserve(skb, hlen);    //留出源、目的mac地址的空间skb_reset_network_header(skb);//设置三层头部指针arp = (struct arphdr *) skb_put(skb, arp_hdr_len(dev));//设置arp头指针skb->dev = dev;skb->protocol = htons(ETH_P_ARP);if (src_hw == NULL)//设置源、目的mac地址src_hw = dev->dev_addr;if (dest_hw == NULL)dest_hw = dev->broadcast;/**   Fill the device header for the ARP frame*///通过调用eth_header,填充二层头部if (dev_hard_header(skb, dev, ptype, dest_hw, src_hw, skb->len) < 0)goto out;/** Fill out the arp protocol part.** The arp hardware type should match the device type, except for FDDI,* which (according to RFC 1390) should always equal 1 (Ethernet).*//** Exceptions everywhere. AX.25 uses the AX.25 PID value not the*  DIX code for the protocol. Make these device structure fields.*/switch (dev->type) {//设置硬件协议类型与软件协议类型,对于Ethernet硬件类型为1软件类型为0x0800default:arp->ar_hrd = htons(dev->type);arp->ar_pro = htons(ETH_P_IP);break;#if IS_ENABLED(CONFIG_AX25)case ARPHRD_AX25:arp->ar_hrd = htons(ARPHRD_AX25);arp->ar_pro = htons(AX25_P_IP);break;#if IS_ENABLED(CONFIG_NETROM)case ARPHRD_NETROM:arp->ar_hrd = htons(ARPHRD_NETROM);arp->ar_pro = htons(AX25_P_IP);break;
#endif
#endif#if IS_ENABLED(CONFIG_FDDI)case ARPHRD_FDDI:arp->ar_hrd = htons(ARPHRD_ETHER);arp->ar_pro = htons(ETH_P_IP);break;
#endif}//设置硬件协议长度、软件协议长度、arp包类型arp->ar_hln = dev->addr_len;arp->ar_pln = 4;arp->ar_op = htons(type);//设置arp的源mac、ip 与目的mac、ip地址arp_ptr = (unsigned char *)(arp + 1);memcpy(arp_ptr, src_hw, dev->addr_len);arp_ptr += dev->addr_len;memcpy(arp_ptr, &src_ip, 4);arp_ptr += 4;switch (dev->type) {
#if IS_ENABLED(CONFIG_FIREWIRE_NET)case ARPHRD_IEEE1394:break;
#endifdefault:if (target_hw != NULL)memcpy(arp_ptr, target_hw, dev->addr_len);elsememset(arp_ptr, 0, dev->addr_len);arp_ptr += dev->addr_len;}memcpy(arp_ptr, &dest_ip, 4);return skb;out:kfree_skb(skb);return NULL;
}
EXPORT_SYMBOL(arp_create);

2.2 arp_send() 发送场景

对于arp_send,既可以发送arp请求数据包,也可以发送arp应答报文,主要是在arp_process中调用。对于应答报文,回复的依据为:

  1. 对于重复地址检测请求,则发送一个arp reply消息。
  2. 对于发往本地的arp request,则发送一个arp reply消息,并将邻居项的状态设置为NUD_STALE。

3 arp 邻居项的创建 + arp solicit 请求发送流程

3.1 创建+发送流程

参见《linux内核协议栈 邻居协议之通用邻居项的状态机机制【核心】》中的第4节

3.2 arp请求报文发送 arp_solicit()

在neigh_resolve_output里就会调用到__neigh_event_send() 判断数据包是否可以直接发送出去,如果此时邻居项的状态为NUD_NONE,则会将邻居项的状态设置为NUD_INCOMPLETE,并将要发送的数据包缓存到邻居项的队列中。在该函数的结尾处通过来判断变量 immediate_probe 来决定是否立刻调用 neigh_probe() 来发送arp请求。而 immediate_probe 变量的置位就是将邻居的状态由 “NUD_NONE” 迁移至 "NUD_INCOMPLETE"时进行的。

对于arp 来说,其在 neigh_probe() 调用 neigh->ops->solicit 即为arp_solicit。

在分析之前,首先需要理解 arp announce的级别,当发送arp请求的主机对应的ip地址不止一个时,以下是 arp announce 级别决定如何选择ip地址:

  • 0:任何ip地址都可以
  • 1:尽可能选择和目的ip处于同一个子网的ip地址,否则使用级别2的选择
  • 2:优先使用主地址
static void arp_solicit(struct neighbour *neigh, struct sk_buff *skb)
{__be32 saddr = 0;u8 dst_ha[MAX_ADDR_LEN], *dst_hw = NULL;struct net_device *dev = neigh->dev;__be32 target = *(__be32 *)neigh->primary_key;int probes = atomic_read(&neigh->probes);struct in_device *in_dev;rcu_read_lock();//获取源设备ip层的相关信息in_dev = __in_dev_get_rcu(dev);if (!in_dev) {rcu_read_unlock();return;}switch (IN_DEV_ARP_ANNOUNCE(in_dev)) {default:/*判断数据包的源地址是否为本地地址。若是,则将源地址设置为数据包的源地址;若不是,则调用inet_select_addr选择一个源地址*/case 0:     /* By default announce any local IP */if (skb && inet_addr_type(dev_net(dev),ip_hdr(skb)->saddr) == RTN_LOCAL)saddr = ip_hdr(skb)->saddr;break;case 1:     /* Restrict announcements of saddr in same subnet *//*判断数据包的源地址是否为本地地址。若是,则优先选择与目的ip地址在相同子网上的ip地址,否则则调用inet_select_addr优先使用主地址*/if (!skb)break;saddr = ip_hdr(skb)->saddr;if (inet_addr_type(dev_net(dev), saddr) == RTN_LOCAL) {/* saddr should be known to target */if (inet_addr_onlink(in_dev, target, saddr))break;}saddr = 0;break;case 2:       /* Avoid secondary IPs, get a primary/preferred one */break;//调用inet_select_addr优先获取一个符合条件的主地址}rcu_read_unlock();/*若此时还没有设置源ip地址,则调用inet_select_addr获取ip地址,该函数主要实现的功能:1、对于指定设备dev所关联的ip配置块,查找scope小于等于RT_SCOPE_LINK且与目的地址属于同一子网的地址作为源ip地址2、对于符合scope条件的ip地址,若没有子网相同的地址,则选择主地址作为源ip地址3、对于在指定设备dev上找不到满足scope条件的ifaddr结构,则遍历所有dev设备,找到符合条件的ifaddr结构,并将其主地址作为源ip地址。*/if (!saddr)saddr = inet_select_addr(dev, target, RT_SCOPE_LINK);probes -= neigh->parms->ucast_probes;if (probes < 0) {//判断arp请求报文是否到达上限,若到达上限则不发送if (!(neigh->nud_state & NUD_VALID))pr_debug("trying to ucast probe in NUD_INVALID\n");neigh_ha_snapshot(dst_ha, neigh, dev);dst_hw = dst_ha;} else {probes -= neigh->parms->app_probes;if (probes < 0) {
#ifdef CONFIG_ARPDneigh_app_ns(neigh);
#endifreturn;}}//调用arp_send发送arp请求包arp_send(ARPOP_REQUEST, ETH_P_ARP, target, dev, saddr,dst_hw, dev->dev_addr, NULL);
}

linux内核协议栈 邻居协议之 arp 数据包收发处理流程相关推荐

  1. linux内核协议栈 TCP数据发送之发送窗口

    目录 1 发送窗口概述 2 snd_una 和 snd_wnd 的更新 2.1 发送窗口初始化 2.1.1 客户端初始化 2.1.2 服务器端初始化 2.2 本地接收窗口 rcv_wnd 通告 2.2 ...

  2. linux网卡发送数据包流程,linux内核Ethernet以太网卡驱动收发数据过程

    linux内核Ethernet以太网卡驱动收发数据过程 linux内核Ethernet以太网卡驱动收发数据过程 下图简单描述了网卡驱动与Linux内核之间的联系: 关于上图的一些说明: 系统初始化: ...

  3. linux内核协议栈 TCP层数据发送之TSO/GSO

    目录 1 基本概念 2 TCP延迟分段判定 2.1 客户端初始化 2.2 服务器端初始化 2.3 sk_setup_caps() 3 整体结构 4. TCP发送路径TSO处理 4.1 tcp_send ...

  4. linux内核协议栈 netfilter 之连接跟踪子系统的L3 L4协议栈模块初始化与自定义注册

    目录 1 L3.L4协议跟踪初始化 nf_conntrack_proto_init() 1.1 L3协议管理 1.1.1 struct nf_conntrack_l3proto 1.1.2 L3协议注 ...

  5. linux红黑树节点没有数据,真正理解红黑树,真正的(Linux内核里大量用到的数据 -电脑资料...

    作为一种数据结构,红黑树可谓不算朴素,因为各种宣传让它过于神秘,网上搜罗了一大堆的关于红黑树的文章,不外乎千篇一律,介绍概念,分析性能,贴上代码,然后给上罪恶的一句话,它最坏情况怎么怎么地... 1. ...

  6. Linux内核分析 - 网络[一]:收发数据包的调用

    什么是NAPI NAPI是linux一套最新的处理网口数据的API,linux 2.5引入的,所以很多驱动并不支持这种操作方式.简单来说,NAPI是综合中断方式与轮询方式的技术.数据量很低与很高时,N ...

  7. Linux内核数据调用,Linux内核分析 - 网络[一]:收发数据包的调用

    什么是NAPI NAPI是linux一套最新的处理网口数据的API,linux 2.5引入的,所以很多驱动并不支持这种操作方式.简单来说,NAPI是综合中断方式与轮询方式的技术.数据量很低与很高时,N ...

  8. java udp包_基于UDP协议的数据包收发程序(代码+报告)Java

    [实例简介] 设计要求: 1)按照UDP协议数据包发送方式实现用户端之间的通信. 2)统计包的发送和接收数,计算数据包的丢失数. 3)设计美观易用的图形界面. [实例截图] [核心代码] 基于UDP协 ...

  9. linux内核定时器死机,浅析linux内核中timer定时器的生成和sofirq软中断调用流程

    浅析linux内核中timer定时器的生成和sofirq软中断调用流程 mod_timer添加的定时器timer在内核的软中断中发生调用,__run_timers会spin_lock_irq(& ...

最新文章

  1. iOS开发笔记 -- 推送证书的创建及合并
  2. js new Date()不带时分秒时,时间变了 问题解决
  3. Python 实现杨辉三角
  4. mac 如何查看anaconda的路径_Mac OS如何直接查看gif图片?分享MAC直接查看gif图片的三种方法...
  5. libsvm的安装和使用(1)
  6. [公告][重要]Senparc.Weixin v4.9.0 Senparc.Weixin.MP v14.3.104更新说明
  7. docker安装与学习
  8. Pytorch 为什么每一轮batch需要设置optimizer.zero_grad
  9. apollo源码分析 感知_Kitty中的动态线程池支持Nacos,Apollo多配置中心了
  10. 论文 计算机动态网页的制作,毕业论文 动态网页制作学习网站的设计与实现
  11. 调试比较大小的 才=c语言,计算机等级二级C语言考试练习题(六)
  12. 如何成为一名好的程序员的一些个人经验
  13. unity使用TUIO协议
  14. CodeForces-1040B Shashlik Cooking(贪心)
  15. 手机照片局部放大镜_往事洗照片
  16. 完数什么意思_【寒假预习】人教版五年级数学(下册)知识点学习要点
  17. Unity 检测手机性能,区分高中低端机型
  18. zigbee终端入网
  19. 异常解决:在实体引用中, 实体名称必须紧跟在 ‘‘ 后面
  20. 运维开发工程师的必备技能总结

热门文章

  1. SNMP和LLDP获取网络邻居节点
  2. 【路径规划】基于UKF和MPC实现无人机编队路径避碰matlab源码
  3. python变量作用域和生存期_变量的生存期和作用域
  4. 中科大计算机视觉就业前景,挑战Deepfake中科大斩获亚军,与第一名仅差0.0005
  5. java服务端获取客户端ip(代理)
  6. 【git】使用git remote进行仓库迁移
  7. 计算机辅助药物设计在新药研发中的应用,计算机辅助药物设计在抗耐药菌药物研发中的应用进展...
  8. [VulnHub靶机]Lin.Security_linux提权
  9. 机房环境监控什么意思?机房环境监控系统作用
  10. 69天突破100万用户,韩国5G为何如此生猛?