-
C语言语法的设计在类型声明上有一个简单的原则, 即"变量怎么使用就怎么声明"。C语言声明的优先级规则如下:
A. 解析声明从名字开始, 然后按照优先级顺序依次读取。B. 优先级从高到低依次是: B.1 括号括起来的部分优先级最高。B.2 后缀操作符(括号"()"表示函数,"[]"表示数组)B.3 前缀操作符*表示指针。C. Const和Volatile后紧跟类型符(int, long等)则作用于类型说明符,其他情况下作用于其左边的紧邻的指针星号。
利用上述规则分析 char* const *(*next)(); 1. 首先从名字next开始. 2. next前边的*表示next是一个指针。 3. 然后把*next看做一个整体, 括号括起来, 其前面是一个*, 后边是(). 后缀优先, 所以"()"表示是一个函数。 4. 由前缀*看知道此函数的返回值是一个指针. 5. 是一个指向char* const类型的指针。
另一个例子:char *(*(**foo [][8])())[] 的分析过程如下
char *(*(**foo [][8])())[]
char *(*(**foo [][8])())[] foo 是 … char
char *(*(**foo [][8])())[] foo 是 一个数组 … char
char *(*(**foo [][8])())[] foo 是 一个数组且成员为 8维数组 … char
char *(*(**foo [][8])())[] foo 是 一个数组且成员为 8维数组且成员为 指针且指向 … char
char *(*(**foo [][8])())[] foo 是 一个数组且成员为 8维数组且成员为 指针且指向 指针且指向 … char
char *(*(**foo [][8])())[] foo 是 一个数组且成员为 8维数组且成员为 指针且指向 指针且指向 函数且返回 … char
char *(*(**foo [][8])())[] foo 是 一个数组且成员为 8维数组且成员为 指针且指向 指针且指向 函数且返回 指针且指向 … char
char *(*(**foo [][8])())[] foo 是 一个数组且成员为 8维数组且成员为 指针且指向 指针且指向 函数且返回 指针且指向 数组 … char
char *(*(**foo [][8])())[] foo 是 一个数组且成员为 8维数组且成员为 指针且指向 指针且指向 函数且返回 指针且指向 数组且成员为 指针且指向 char
一些其他的规则:1. 函数的返回值不能是一个函数,所以 foo()() 非法。2. 函数的返回值不能是一个数组,所以 foo()[] 非法 3. 数组里面不能有函数所以 foo[]()非法。
1. 函数的返回值允许是一个函数指针,所以 int(*fun())() 合法。2. 函数的返回值允许上一个数组的指针,所以 int(*foo*()[] 合法 3. 数组里面运行有函数指针所以 int(*foo[])()合法 4. 数组里允许有其他数组,所以 int foo[][] 合法。
typedef int(*fun())() 定义fun为一个函数指针, 返回int, 参数为空。这正体现了怎么使用就怎么声明的原则. 例如: char (*Jack)[20] 就定义了一个Jack, 可以这样使用"Jack=(char (*) [20]) malloc(20)" 也可以如下使用
typedef char (*Jack)[20]; Jack x = (Jack)malloc(20);
-
2009-02-21
三层交换原理及与二层交换的比较 - [网络]
在今天的网络建设中,三层交换机以其高效的性能、优良的性能价格比得到用户的认可和赞许。目前,三层交换机在企业网、校园网建设、智能社区接入等等许多场合中得到了大量的应用,市场的需求和技术的更新推动这种应用向纵深发展。
传统二层(工作在OSI7层模型的第二层)交换技术
传统的局域网交换机是一种二层网络设备,它在操作过程中不断收集信息去建立起它本身的一个MAC地址表。这个表相当简单,基本上说明了某个MAC 地址是在哪个端口上被发现的。这样当交换机收到一个以太网包时,它便会查看一下该以太网包的目的MAC地址,核对一下自己的地址表以确认该从哪个端口把包发出去。但当交换机收到一个不认识的包时,也就是说如果目的MAC地址不在MAC地址表中,交换机便会把该包“扩散”出去,即从所有端口发出去,就如同交换机收到一个广播包一样,这就暴露出传统局域网交换机的弱点:不能有效的解决广播、异种网络互连、安全性控制等问题。因此,产生了交换机上的VLAN(虚拟局域网)技术。
三层(工作在OSI7层模型的第三层)交换技术
三层交换(也称多层交换技术,或IP交换技术)是相对于传统交换概念而提出的。众所周知,传统的交换技术是在OSI网络标准模型中的第二层――数据链路层进行操作的,而三层交换技术在网络模型中的第三层实现了分组的高速转发。简单的说,三层交换技术就是“二层交换技术 + 三层转发”。三层交换技术的出现,解决了局域网中网段划分之后网段中的子网必须依赖路由器进行管理的局面,解决了传统路由器低速、复杂所造成的网络瓶颈问题。
二层交换机通讯过程
假设两个使用IP协议的站点A、B通过第二层交换机进行通信,发送站点A在开始发送时,会先拿自己的IP地址与B站的IP地址进行比较,判断B站是否与自己在同一子网内。若目的站B与发送站A在同一子网内,则进行二层的转发。具体步骤如下:为了得到站点B的 MAC地址,站点A首先发一个ARP广播报文,请求站点B的MAC地址。该ARP请求报文进入交换机后,首先进行源MAC地址学习,芯片自动把站点A的MAC地址以及进入交换机的端口号等信息填入到芯片的MAC地址表中,然后在MAC地址表中进行目的地址查找。由于此时是一个广播报文,交换机则会把这个广播报文从进入交换机端口所属的VLAN中进行广播。B站点收到这个ARP请求报文之后,会立刻发送一个ARP回复报文,这个报文是一个单播报文,目的地址为站点A的MAC地址。该包进入交换机后,同样,首先进行源MAC地址学习,然后进行目的地址查找,由于此时MAC地址表中已经存在了A站点MAC地址的匹配条目,所以交换机直接把此报文从相应的端口中转发出去。通过以上一次ARP过程,交换芯片就把站点A和B的信息保存在其MAC地址表中。以后A、B之间进行通信或者同一网段的其它站点想要与A或B通信,交换机就知道该把报文从哪个端口送出。从以上过程可以看出,所有二层转发都是由硬件完成的,无论是MAC地址表的学习过程还是目的地址查找确定输出端口过程都没有软件进行干预。
三层交换机通讯过程
站点A、B通过三层交换机进行通信。站点A和B所在网段都属于交换机上的直连网段,若站点A和站点B不在同一子网内,发送站A首先要向其“缺省网关”发出ARP请求报文,而“缺省网关”的IP地址其实就是三层交换机上站点A所属VLAN的IP地址。当发送站A对“缺省网关”的IP地址广播出一个ARP请求时,交换机就向发送站A回一个ARP回复报文,告诉站点A交换机此VLAN的MAC地址,同时可以通过软件把站点A的IP地址、MAC地址、与交换机直接相连的端口号等信息设置到交换芯片的三层硬件表项中。站点A收到这个ARP回复报文之后,进行目的MAC地址替换,把要发给B的包首先发给交换机。交换机收到这个包以后,同样首先进行源MAC地址学习,目的MAC地址查找,由于此时目的MAC地址为交换机的MAC地址,在这种情况下将会把该报文送到交换芯片的三层引擎处理。一般来说,三层引擎会有两个表,一个是主机路由表,这个表是以IP地址为索引的,里面存放目的IP地址、下一跳MAC地址、端口号等信息。若找到一条匹配表项,就会在对报文进行一些操作(例如目的MAC与源MAC替换、TTL减1等)之后将报文从表中指定的端口转发出去。若主机路由表中没有找到匹配条目,则会继续查找另一个表――网段路由表。这个表存放网段地址、下一跳MAC地址、端口号等信息。一般来说这个表的条目要少得多,但覆盖的范围很大,只要设置得当,基本上可以保证大部分进入交换机的报文都走硬件转发,这样不仅大大提高转发速度,同时也减轻了CPU的负荷。由于芯片内部的三层引擎中已经保存站点A、B的路由信息,以后站点A、B之间进行通信或其它网段的站点想要与A、B进行通信,交换芯片则会直接把包从三层硬件表项中指定的端口转发出去,而不必再把包交给CPU处理。这种通过“一次路由,多次交换”的方式,大大提高了转发速度。
三层交换实现的例子:每个VLAN对应一个IP网段。在二层上,VLAN之间是隔离的,这点跟二层交换机中交换引擎的功能是一模一样的。不同IP 网段之间的访问要跨越VLAN,要使用三层转发引擎提供的VLAN间路由功能。在使用二层交换机和路由器的组网中,每个需要与其他IP网段通信的IP网段都需要使用一个路由器接口作为网关。而第三层转发引擎就相当于传统组网中的路由器,当需要与其他VLAN通信时也要在三层交换引擎上分配一个路由接口,用来做VLAN的网关。三层交换机上的这个路由接口是在三层转发引擎和二层转发引擎上的,是通过配置转发芯片来实现的,与路由器的接口不同,它是不可见的。假设两个使用IP协议的站点A、B通过第三层交换机进行通信,发送站A在开始发送时,把自己的IP地址与B站的IP地址比较,判断B站是否与自己在同一子网内,若目的站B与发送站A在同一子网内,则进行二层的转发,若两个站点不在同一子网内,如发送站A要与目的站B通信,发送站A要向三层交换机的三层交换模块发出ARP(地址解析)封包。当发送站A对三层交换模块的IP地址广播出一个ARP请求时,如果三层交换模块在以前的通信过程中已经知道B站的MAC地址(应该还有交换机的端口号等等信息),则向发送站A回复B的MAC地址,否则三层交换模块根据路由信息向B站广播一个ARP请求,B站得到此ARP请求后向三层交换模块回复其MAC地址,三层交换模块保存此地址并回复给发送站A,同时将B站的MAC地址发送到二层交换引擎的MAC地址表中。从这以后,A向B发送的数据包便全部交给二层交换处理,信息得以高速交换。可见由于仅仅在路由过程中才需要三层处理,绝大部分数据都通过二层交换转发,三层交换机的速度很快,接近二层交换机的速度。
三层交换从概念的提出到今天的普及应用,虽然只历经了几年的时间,但其在网络建设中的应用越来越广泛,从最初骨干层、中间的汇聚层一直渗透到边缘的接入层。三层交换机具有速度快、性能好、价格低等众多的优势。凡是没有广域网连接需求,同时又需要路由器的地方,都可以用三层交换机代替。随着ASIC硬件芯片技术的发展和实际应用的推广,三层交换的技术与产品会得到进一步发展。
-
2009-02-20
TCP Keepalive HOWTO under linux - [网络]
1. Introduction
Understanding TCP keepalive is not necessary in most cases, but it's a subject that can be very useful under particular circumstances. You will need to know basic TCP/IP networking concepts, and the C programming language to understand all sections of this document.
The main purpose of this HOWTO is to describe TCP keepalive in detail and demonstrate various application situations. After some initial theory, the discussion focuses on the Linux implementation of TCP keepalive routines in the modern Linux kernel releases (2.4.x, 2.6.x), and how system administrators can take advantage of these routines, with specific configuration examples and tricks.
The second part of the HOWTO involves the programming interface exposed by the Linux kernel, and how to write TCP keepalive-enabled applications in the C language. Pratical examples are presented, and there is an introduction to the libkeepalive project, which permits legacy applications to benefit from keepalive with no code modification.
1.1. Copyright and License
This document, TCP Keepalive HOWTO, is copyrighted (c) 2007 by Fabio Busatto. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is available at http://www.gnu.org/copyleft/fdl.html.
Source code included in this document is released under the terms of the GNU General Public License, Version 2 or any later version published by the Free Software Foundation. A copy of the license is available at http://www.gnu.org/copyleft/gpl.html.
Linux is a registered trademark of Linus Torvalds.
1.2. Disclaimer
No liability for the contents of this document can be accepted. Use the concepts, examples and information at your own risk. There may be errors and inaccuracies that could be damaging to your system. Proceed with caution, and although this is highly unlikely, the author does not take any responsibility.
All copyrights are held by their by their respective owners, unless specifically noted otherwise. Use of a term in this document should not be regarded as affecting the validity of any trademark or service mark. Naming of particular products or brands should not be seen as endorsements.
1.3. Credits / Contributors
This work is not especially related to any people that I should thank. But my life is, and my knowledge too: so, thanks to everyone that has supported me, prior to my birth, now, and in the future. Really.
A special thank is due to Tabatha, the patient woman that read my work and made the needed reviews.
1.4. Feedback
Feedback is most certainly welcome for this document. Send your additions, comments and criticisms to the following email address: <fabio.busatto@sikurezza.org>.
1.5. Translations
There are no translated versions of this HOWTO at the time of publication. If you are interested in translating this HOWTO into other languages, please feel free to contact me. Your contribution will be very welcome.
2. TCP keepalive overview
In order to understand what TCP keepalive (which we will just call keepalive) does, you need do nothing more than read the name: keep TCP alive. This means that you will be able to check your connected socket (also known as TCP sockets), and determine whether the connection is still up and running or if it has broken.
2.1. What is TCP keepalive?
The keepalive concept is very simple: when you set up a TCP connection, you associate a set of timers. Some of these timers deal with the keepalive procedure. When the keepalive timer reaches zero, you send your peer a keepalive probe packet with no data in it and the ACK flag turned on. You can do this because of the TCP/IP specifications, as a sort of duplicate ACK, and the remote endpoint will have no arguments, as TCP is a stream-oriented protocol. On the other hand, you will receive a reply from the remote host (which doesn't need to support keepalive at all, just TCP/IP), with no data and the ACK set.
If you receive a reply to your keepalive probe, you can assert that the connection is still up and running without worrying about the user-level implementation. In fact, TCP permits you to handle a stream, not packets, and so a zero-length data packet is not dangerous for the user program.
This procedure is useful because if the other peers lose their connection (for example by rebooting) you will notice that the connection is broken, even if you don't have traffic on it. If the keepalive probes are not replied to by your peer, you can assert that the connection cannot be considered valid and then take the correct action.
2.2. Why use TCP keepalive?
You can live quite happily without keepalive, so if you're reading this, you may be trying to understand if keepalive is a possible solution for your problems. Either that or you've really got nothing more interesting to do instead, and that's okay too. :)
Keepalive is non-invasive, and in most cases, if you're in doubt, you can turn it on without the risk of doing something wrong. But do remember that it generates extra network traffic, which can have an impact on routers and firewalls.
In short, use your brain and be careful.
In the next section we will distinguish between the two target tasks for keepalive:
-
Checking for dead peers
-
Preventing disconnection due to network inactivity
2.3. Checking for dead peers
Keepalive can be used to advise you when your peer dies before it is able to notify you. This could happen for several reasons, like kernel panic or a brutal termination of the process handling that peer. Another scenario that illustrates when you need keepalive to detect peer death is when the peer is still alive but the network channel between it and you has gone down. In this scenario, if the network doesn't become operational again, you have the equivalent of peer death. This is one of those situations where normal TCP operations aren't useful to check the connection status.
Think of a simple TCP connection between Peer A and Peer B: there is the initial three-way handshake, with one SYN segment from A to B, the SYN/ACK back from B to A, and the final ACK from A to B. At this time, we're in a stable status: connection is established, and now we would normally wait for someone to send data over the channel. And here comes the problem: unplug the power supply from B and instantaneously it will go down, without sending anything over the network to notify A that the connection is going to be broken. A, from its side, is ready to receive data, and has no idea that B has crashed. Now restore the power supply to B and wait for the system to restart. A and B are now back again, but while A knows about a connection still active with B, B has no idea. The situation resolves itself when A tries to send data to B over the dead connection, and B replies with an RST packet, causing A to finally to close the connection.
Keepalive can tell you when another peer becomes unreachable without the risk of false-positives. In fact, if the problem is in the network between two peers, the keepalive action is to wait some time and then retry, sending the keepalive packet before marking the connection as broken.
_____ _____ | | | | | A | | B | |_____| |_____| ^ ^ |--->--->--->-------------- SYN -------------->--->--->---| |---<---<---<------------ SYN/ACK ------------<---<---<---| |--->--->--->-------------- ACK -------------->--->--->---| | | | system crash ---> X | | system restart ---> ^ | | |--->--->--->-------------- PSH -------------->--->--->---| |---<---<---<-------------- RST --------------<---<---<---| | |2.4. Preventing disconnection due to network inactivity
The other useful goal of keepalive is to prevent inactivity from disconnecting the channel. It's a very common issue, when you are behind a NAT proxy or a firewall, to be disconnected without a reason. This behavior is caused by the connection tracking procedures implemented in proxies and firewalls, which keep track of all connections that pass through them. Because of the physical limits of these machines, they can only keep a finite number of connections in their memory. The most common and logical policy is to keep newest connections and to discard old and inactive connections first.
Returning to Peers A and B, reconnect them. Once the channel is open, wait until an event occurs and then communicate this to the other peer. What if the event verifies after a long period of time? Our connection has its scope, but it's unknown to the proxy. So when we finally send data, the proxy isn't able to correctly handle it, and the connection breaks up.
Because the normal implementation puts the connection at the top of the list when one of its packets arrives and selects the last connection in the queue when it needs to eliminate an entry, periodically sending packets over the network is a good way to always be in a polar position with a minor risk of deletion.
_____ _____ _____ | | | | | | | A | | NAT | | B | |_____| |_____| |_____| ^ ^ ^ |--->--->--->---|----------- SYN ------------->--->--->---| |---<---<---<---|--------- SYN/ACK -----------<---<---<---| |--->--->--->---|----------- ACK ------------->--->--->---| | | | | | <--- connection deleted from table | | | | |--->- PSH ->---| <--- invalid connection | | | |3. Using TCP keepalive under Linux
Linux has built-in support for keepalive. You need to enable TCP/IP networking in order to use it. You also need procfs support and sysctl support to be able to configure the kernel parameters at runtime.
The procedures involving keepalive use three user-driven variables:
- tcp_keepalive_time
-
the interval between the last data packet sent (simple ACKs are not considered data) and the first keepalive probe; after the connection is marked to need keepalive, this counter is not used any further
- tcp_keepalive_intvl
-
the interval between subsequential keepalive probes, regardless of what the connection has exchanged in the meantime
- tcp_keepalive_probes
-
the number of unacknowledged probes to send before considering the connection dead and notifying the application layer
Remember that keepalive support, even if configured in the kernel, is not the default behavior in Linux. Programs must request keepalive control for their sockets using the setsockopt interface. There are relatively few programs implementing keepalive, but you can easily add keepalive support for most of them following the instructions explained later in this document.
3.1. Configuring the kernel
There are two ways to configure keepalive parameters inside the kernel via userspace commands:
-
procfs interface
-
sysctl interface
We mainly discuss how this is accomplished on the procfs interface because it's the most used, recommended and the easiest to understand. The sysctl interface, particularly regarding the sysctl(2) syscall and not the sysctl(8) tool, is only here for the purpose of background knowledge.
3.1.1. The procfs interface
This interface requires both sysctl and procfs to be built into the kernel, and procfs mounted somewhere in the filesystem (usually on /proc, as in the examples below). You can read the values for the actual parameters by "catting" files in /proc/sys/net/ipv4/ directory:
# cat /proc/sys/net/ipv4/tcp_keepalive_time 7200 # cat /proc/sys/net/ipv4/tcp_keepalive_intvl 75 # cat /proc/sys/net/ipv4/tcp_keepalive_probes 9The first two parameters are expressed in seconds, and the last is the pure number. This means that the keepalive routines wait for two hours (7200 secs) before sending the first keepalive probe, and then resend it every 75 seconds. If no ACK response is received for nine consecutive times, the connection is marked as broken.
Modifying this value is straightforward: you need to write new values into the files. Suppose you decide to configure the host so that keepalive starts after ten minutes of channel inactivity, and then send probes in intervals of one minute. Because of the high instability of our network trunk and the low value of the interval, suppose you also want to increase the number of probes to 20.
Here's how we would change the settings:
# echo 600 > /proc/sys/net/ipv4/tcp_keepalive_time # echo 60 > /proc/sys/net/ipv4/tcp_keepalive_intvl # echo 20 > /proc/sys/net/ipv4/tcp_keepalive_probesTo be sure that all succeeds, recheck the files and confirm these new values are showing in place of the old ones.
Remember that procfs handles special files, and you cannot perform any sort of operation on them because they're just an interface within the kernel space, not real files, so try your scripts before using them, and try to use simple access methods as in the examples shown earlier.
You can access the interface through the sysctl(8) tool, specifying what you want to read or write.
# sysctl \ > net.ipv4.tcp_keepalive_time \ > net.ipv4.tcp_keepalive_intvl \ > net.ipv4.tcp_keepalive_probes net.ipv4.tcp_keepalive_time = 7200 net.ipv4.tcp_keepalive_intvl = 75 net.ipv4.tcp_keepalive_probes = 9Note that sysctl names are very close to procfs paths. Write is performed using the -w switch of sysctl (8):
# sysctl -w \ > net.ipv4.tcp_keepalive_time=600 \ > net.ipv4.tcp_keepalive_intvl=60 \ > net.ipv4.tcp_keepalive_probes=20 net.ipv4.tcp_keepalive_time = 600 net.ipv4.tcp_keepalive_intvl = 60 net.ipv4.tcp_keepalive_probes = 20Note that sysctl (8) doesn't use sysctl(2) syscall, but reads and writes directly in the procfs subtree, so you will need procfs enabled in the kernel and mounted in the filesystem, just as you would if you directly accessed the files within the procfs interface. Sysctl(8) is just a different way to do the same thing.
3.1.2. The sysctl interface
There is another way to access kernel variables: sysctl(2 ) syscall. It can be useful when you don't have procfs available because the communication with the kernel is performed directly via syscall and not through the procfs subtree. There is currently no program that wraps this syscall (remember that sysctl(8) doesn't use it).
For more details about using sysctl(2) refer to the manpage.
3.2. Making changes persistent to reboot
There are several ways to reconfigure your system every time it boots up. First, remember that every Linux distribution has its own set of init scripts called by init (8). The most common configurations include the /etc/rc.d/ directory, or the alternative, /etc/init.d/. In any case, you can set the parameters in any of the startup scripts, because keepalive rereads the values every time its procedures need them. So if you change the value of tcp_keepalive_intvl when the connection is still up, the kernel will use the new value going forward.
There are three spots where the initialization commands should logically be placed: the first is where your network is configured, the second is the rc.local script, usually included in all distributions, which is known as the place where user configuration setups are done. The third place may already exist in your system. Referring back to the sysctl (8) tool, you can see that the -p switch loads settings from the /etc/sysctl.conf configuration file. In many cases your init script already performs the sysctl -p (you can "grep" it in the configuration directory for confirmation), and so you just have to add the lines in /etc/sysctl.conf to make them load at every boot. For more information about the syntax of sysctl.conf(5), refer to the manpage.
4. Programming applications
This section deals with programming code needed if you want to create applications that use keepalive. This is not a programming manual, and it requires that you have previous knowledge in C programming and in networking concepts. I consider you familiar with sockets, and with everything concerning the general aspects of your application.
4.1. When your code needs keepalive support
Not all network applications need keepalive support. Remember that it is TCP keepalive support. So, as you can imagine, only TCP sockets can take advantage of it.
The most beautiful thing you can do when writing an application is to make it as customizable as possible, and not to force decisions. If you want to consider the happiness of your users, you should implement keepalive and let the users decide if they want to use it or not by using a configuration parameter or a switch on the command line.
4.2. The setsockopt function call
All you need to enable keepalive for a specific socket is to set the specific socket option on the socket itself. The prototype of the function is as follows:
int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)The first parameter is the socket, previously created with the socket(2); the second one must be SOL_SOCKET, and the third must be SO_KEEPALIVE . The fourth parameter must be a boolean integer value, indicating that we want to enable the option, while the last is the size of the value passed before.
According to the manpage, 0 is returned upon success, and -1 is returned on error (and errno is properly set).
There are also three other socket options you can set for keepalive when you write your application. They all use the SOL_TCP level instead of SOL_SOCKET, and they override system-wide variables only for the current socket. If you read without writing first, the current system-wide parameters will be returned.
-
TCP_KEEPCNT: overrides tcp_keepalive_probes
-
TCP_KEEPIDLE: overrides tcp_keepalive_time
-
TCP_KEEPINTVL: overrides tcp_keepalive_intvl
4.3. Code examples
This is a little example that creates a socket, shows that keepalive is disabled, then enables it and checks that the option was effectively set.
/* --- begin of keepalive test program --- */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> int main(void); int main() { int s; int optval; socklen_t optlen = sizeof(optval); /* Create the socket */ if((s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { perror("socket()"); exit(EXIT_FAILURE); } /* Check the status for the keepalive option */ if(getsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &optval, &optlen) < 0) { perror("getsockopt()"); close(s); exit(EXIT_FAILURE); } printf("SO_KEEPALIVE is %s\n", (optval ? "ON" : "OFF")); /* Set the option active */ optval = 1; optlen = sizeof(optval); if(setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) { perror("setsockopt()"); close(s); exit(EXIT_FAILURE); } printf("SO_KEEPALIVE set on socket\n"); /* Check the status again */ if(getsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &optval, &optlen) < 0) { perror("getsockopt()"); close(s); exit(EXIT_FAILURE); } printf("SO_KEEPALIVE is %s\n", (optval ? "ON" : "OFF")); close(s); exit(EXIT_SUCCESS); } /* --- end of keepalive test program --- */5. Adding support to third-party software
Not everyone is a software developer, and not everyone will rewrite software from scratch if it lacks just one feature. Maybe you want to add keepalive support to an existing application because, though the author might not have thought it important, you think it will be useful.
First, remember what was said about the situations where you need keepalive. Now you'll need to address connection-oriented TCP sockets.
Since Linux doesn't provide the functionality to enable keepalive support via the kernel itself (as BSD-like operating systems often do), the only way is to perform the setsockopt (2) call after socket creation. There are two solutions:
-
source code modification of the original program
-
setsockopt (2) injection using the library preloading technique
5.1. Modifying source code
Remember that keepalive is not program-related, but socket-related, so if you have multiple sockets, you can handle keepalive for each of them separately. The first phase is to understand what the program does and then search the code for each socket in the program. This can be done using grep(1), as follows:
# grep 'socket *(' *.cThis will more or less show you all sockets in the code. The next step is to select only the right ones: you will need TCP sockets, so look for PF_INET (or AF_INET), SOCK_STREAM and IPPROTO_TCP (or more commonly, 0) in the parameters of your socket list, and remove the non-matching ones.
Another way to create a socket is through accept(2). In this case, follow the TCP sockets identified and check if any of these is a listening socket: if positive, keep in mind that accept(2) returns a socket descriptor, which must be inserted in your socket list.
Once you've identified the sockets you can proceed with changes. The most fast & furious patch can be done by simply adding the setsockopt(2 ) function just after the socket creation block. Optionally, you may include additional calls in order to set the keepalive parameters if you don't like the system defaults. Please be careful when implementing error checks and handlers for the function, maybe by copying the style from the original code around it. Remember to set the optval to a non-zero value and to initialize the optlen before invoking the function.
If you have time or you think it would be really cool, try to add complete keepalive support to your program, including a switch on the command line or a configuration parameter to let the user choose whether or not to use keepalive.
5.2. libkeepalive: library preloading
There are often cases where you don't have the ability to modify the source code of an application, or when you have to enable keepalive for all your programs, so patching and recompiling everything is not recommended.
The libkeepalive project was born to help add keepalive support for applications since the Linux kernel doesn't provide the ability to do the same thing natively (like BSD does). The libkeepalive project homepage is http://libkeepalive.sourceforge.net/
It consists of a shared library that overrides the socket system call in most binaries, without the need to recompile or modify them. The technique is based on the preloading feature of the ld.so(8) loader included in Linux, which allows you to force the loading of shared libraries with higher priority than normal. Programs usually use the socket(2) function call located in the glibc shared library; with libkeepalive you can wrap it and inject the setsockopt (2) just after the socket creation, returning a socket with keepalive already set to the main program. Because of the mechanisms used to inject the system call, this doesn't work when the socket function is statically compiled into the binary, as in a program linked with the gcc(1 ) flag -static.
After downloading and installing libkeepalive, you will able to add keepalive support to your programs without the prerequisite of being root, simply setting the LD_PRELOAD environment variable before executing the program. By the way, the superuser can also force the preloading with a global configuration, and the users can then decide to turn it off by setting the KEEPALIVE environment variable to off.
The environment is also used to set specific values for keepalive parameters, so you have the ability to handle each program differently, setting KEEPCNT, KEEPIDLE and KEEPINTVL before starting the application.
Here's an example of libkeepalive usage:
$ test SO_KEEPALIVE is OFF $ LD_PRELOAD=libkeepalive.so \ > KEEPCNT=20 \ > KEEPIDLE=180 \ > KEEPINTVL=60 \ > test SO_KEEPALIVE is ON TCP_KEEPCNT = 20 TCP_KEEPIDLE = 180 TCP_KEEPINTVL = 60And you can use strace (1) to understand what happens:
$ strace test execve("test", ["test"], [/* 26 vars */]) = 0 [..] open("/lib/libc.so.6", O_RDONLY) = 3 [..] socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3 getsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [0], [4]) = 0 close(3) = 0 [..] _exit(0) = ? $ LD_PRELOAD=libkeepalive.so \ > strace test execve("test", ["test"], [/* 27 vars */]) = 0 [..] open("/usr/local/lib/libkeepalive.so", O_RDONLY) = 3 [..] open("/lib/libc.so.6", O_RDONLY) = 3 [..] open("/lib/libdl.so.2", O_RDONLY) = 3 [..] socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) = 3 setsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [1], 4) = 0 setsockopt(3, SOL_TCP, TCP_KEEPCNT, [20], 4) = 0 setsockopt(3, SOL_TCP, TCP_KEEPIDLE, [180], 4) = 0 setsockopt(3, SOL_TCP, TCP_KEEPINTVL, [60], 4) = 0 [..] getsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [1], [4]) = 0 [..] getsockopt(3, SOL_TCP, TCP_KEEPCNT, [20], [4]) = 0 [..] getsockopt(3, SOL_TCP, TCP_KEEPIDLE, [180], [4]) = 0 [..] getsockopt(3, SOL_TCP, TCP_KEEPINTVL, [60], [4]) = 0 [..] close(3) = 0 [..] _exit(0) = ?For more information, visit the libkeepalive project homepage: http://libkeepalive.sourceforge.net/
-
-
2009-02-20
VLAN 实现虚拟工作组的新兴技术 - [网络]
1 VLAN概述
VLAN(Virtual Local Area Network)即虚拟局域网,是一种通过将局域网内的设备逻辑地而不是物理地划分成一个个网段从而实现虚拟工作组的新兴技术。IEEE于1999年颁布了用以标准化VLAN实现方案的802.1Q协议标准草案。
VLAN技术允许网络管理者将一个物理的LAN逻辑地划分成不同的广播域(或称虚拟LAN,即VLAN),每一个VLAN都包含一组有着相同需求的计算机工作站,与物理上形成的LAN有着相同的属性。但由于它是逻辑地而不是物理地划分,所以同一个VLAN内的各个工作站无须被放置在同一个物理空间里,即这些工作站不一定属于同一个物理LAN网段。一个VLAN内部的广播和单播流量都不会转发到其他VLAN中,从而有助于控制流量、减少设备投资、简化网络管理、提高网络的安全性。
VLAN是为解决以太网的广播问题和安全性而提出的一种协议,它在以太网帧的基础上增加了VLAN头,用VLAN ID把用户划分为更小的工作组,限制不同工作组间的用户二层互访,每个工作组就是一个虚拟局域网。虚拟局域网的好处是可以限制广播范围,并能够形成虚拟工作组,动态管理网络。局域网(LAN)通常是一个单独的广播域,主要由Hub、网桥或交换机等网络设备连接同一网段内的所有节点形成。处于同一个局域网之内的网络节点之间可以直接通信,而处于不同局域网段的设备之间的通信则必须经过路由器才能通信。图1所示即为使用路由器构建的典型的局域网环境。

随着网络的不断扩展,接入设备逐渐增多,网络结构也日趋复杂,必须使用更多的路由器才能将不同的用户划分到各自的广播域中,在不同的局域网之间提供网络互联。
但这样做存在两个缺陷:
首先,随着网络中路由器数量的增多,网络时延逐渐加长,从而导致网络数据传输速度的下降。这主要是因为数据在从一个局域网传递到另一个局域网时,必须经过路由器的路由操作: 路由器根据数据包中的相应信息确定数据包的目标地址,然后再选择合适的路径转发出去。
其次,用户是按照它们的物理连接被自然地划分到不同的用户组(广播域)中。这种分割方式并不是根据工作组中所有用户的共同需要和带宽的需求来进行的。因此,尽管不同的工作组或部门对带宽的需求有很大的差异,但它们却被机械地划分到同一个广播域中争用相同的带宽。
VLAN的定义及特点
虚拟局域网VLAN是一组逻辑上的设备和用户,这些设备和用户并不受物理网段的限制,可以根据功能、部门及应用等因素将它们组织起来,相互之间的通信就好像它们在同一个网段中一样,由此得名虚拟局域网。VLAN是一种比较新的技术,工作在OSI参考模型的第2层和第3层,一个VLAN就是一个广播域,VLAN之间的通信是通过第3层的路由器来完成的。与传统的局域网技术相比较,VLAN技术更加灵活,它具有以下优点:
● 网络设备的移动、添加和修改的管理开销减少;
● 可以控制广播活动;
● 可提高网络的安全性。
VLAN在交换机上的实现方法,可以大致划分为4类:
2、 基于端口划分的VLAN
这种划分VLAN的方法是根据以太网交换机的端口来划分,比如Quidway S3526的1~4端口为VLAN 10,5~17为VLAN 20,18~24为VLAN 30,当然,这些属于同一VLAN的端口可以不连续,如何配置,由管理员决定,如果有多个交换机,例如,可以指定交换机 1 的1~6端口和交换机 2 的1~4端口为同一VLAN,即同一VLAN可以跨越数个以太网交换机,根据端口划分是目前定义VLAN的最广泛的方法,IEEE 802.1Q规定了依据以太网交换机的端口来划分VLAN的国际标准。
这种划分的方法的优点是定义VLAN成员时非常简单,只要将所有的端口都指定义一下就可以了。它的缺点是如果VLAN A的用户离开了原来的端口,到了一个新的交换机的某个端口,那么就必须重新定义。基于端口的VLAN的划分是最简单、有效的VLAN划分方法,它按照局域网交换机端口来定义VLAN成员。VLAN从逻辑上把局域网交换机的端口划分开来,从而把终端系统划分为不同的部分,各部分相对独立,在功能上模拟了传统的局域网。基于端口的VLAN又分为在单交换机端口和多交换机端口定义VLAN两种情况:
(1) 单交换机端口定义VLAN
如图2所示,交换机的1、2、6、7、8端口组成VLAN1,3、4、5端口组成了VLAN2。这种VLAN只支持一个交换机。

(2) 多交换机端口定义VLAN
如图3所示,交换机1的1、2、3端口和交换机2的4、5、6端口组成VLAN1,交换机1的4、5、6、7、8端口和交换机2的1、2、3、7、8端口组成VLAN2。

基于端口的VLAN的划分简单、有效,但其缺点是当用户从一个端口移动到另一个端口时,网络管理员必须对VLAN成员进行重新配置。
3、基于MAC地址划分VLAN
这种划分VLAN的方法是根据每个主机的MAC地址来划分,即对每个MAC地址的主机都配置他属于哪个组。这种划分VLAN的方法的最大优点就是当用户物理位置移动时,即从一个交换机换到其他的交换机时,VLAN不用重新配置,所以,可以认为这种根据MAC地址的划分方法是基于用户的VLAN,这种方法的缺点是初始化时,所有的用户都必须进行配置,如果有几百个甚至上千个用户的话,配置是非常累的。而且这种划分的方法也导致了交换机执行效率的降低,因为在每一个交换机的端口都可能存在很多个VLAN组的成员,这样就无法限制广播包了。另外,对于使用笔记本电脑的用户来说,他们的网卡可能经常更换,这样,VLAN就必须不停的配置。
4、基于网络层划分VLAN
这种划分VLAN的方法是根据每个主机的网络层地址或协议类型(如果支持多协议)划分的,虽然这种划分方法是根据网络地址,比如IP地址,但它不是路由,与网络层的路由毫无关系。它虽然查看每个数据包的IP地址,但由于不是路由,所以,没有RIP,OSPF等路由协议,而是根据生成树算法进行桥交换,
这种方法的优点是用户的物理位置改变了,不需要重新配置所属的VLAN,而且可以根据协议类型来划分VLAN,这对网络管理者来说很重要,还有,这种方法不需要附加的帧标签来识别VLAN,这样可以减少网络的通信量。
这种方法的缺点是效率低,因为检查每一个数据包的网络层地址是需要消耗处理时间的(相对于前面两种方法),一般的交换机芯片都可以自动检查网络上数据包的以太网祯头,但要让芯片能检查IP帧头,需要更高的技术,同时也更费时。当然,这与各个厂商的实现方法有关。
5、根据IP组播划分VLAN
IP 组播实际上也是一种VLAN的定义,即认为一个组播组就是一个VLAN,这种划分的方法将VLAN扩大到了广域网,因此这种方法具有更大的灵活性,而且也很容易通过路由器进行扩展,当然这种方法不适合局域网,主要是效率不高。
鉴于当前业界VLAN发展的趋势,考虑到各种VLAN划分方式的优缺点,为了最大程度上地满足用户在具体使用过程中需求,减轻用户在VLAN的具体使用和维护中的工作量,Quidway S系列交换机采用根据端口来划分VLAN的方法。







