<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Soul Of Free Loop &#187; 端口</title>
	<atom:link href="https://zohead.com/archives/tag/port/feed" rel="self" type="application/rss+xml" />
	<link>https://zohead.com</link>
	<description>Uranus Zhou&#039;s Blog</description>
	<lastBuildDate>Sat, 19 Jul 2025 15:42:46 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.8</generator>
	<item>
		<title>IBM Bluemix Docker容器初步体验</title>
		<link>https://zohead.com/archives/ibm-bluemix-docker/</link>
		<comments>https://zohead.com/archives/ibm-bluemix-docker/#comments</comments>
		<pubDate>Thu, 14 Apr 2016 19:20:13 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[Docker]]></category>
		<category><![CDATA[技术]]></category>
		<category><![CDATA[Baseimage-docker]]></category>
		<category><![CDATA[Bluemix]]></category>
		<category><![CDATA[cf ic]]></category>
		<category><![CDATA[Cloud Foundry]]></category>
		<category><![CDATA[IBM]]></category>
		<category><![CDATA[云平台]]></category>
		<category><![CDATA[容器]]></category>
		<category><![CDATA[端口]]></category>

		<guid isPermaLink="false">https://zohead.com/?p=1213</guid>
		<description><![CDATA[现在国内外流行的云平台越来越多，且不谈像国外的 Amazon ECS、AWS 以及 国内的阿里云、BAE、SAE 这些老牌的云服务，目前提供 Docker 容器服务的平台也多起来，国外有 StackEngine、Tutum、IBM Bluemix 等云平台，国内也有 DaoCloud、时速云、灵雀云之类提供 Docker 容器服务的平台。最近对 Docker 也稍微有了点兴趣，比较之下准备拿 IBM Bluemix 来捣鼓 Docker 容器练练手。 本文不打算对 Docker 容器技术本身对什么介绍了，选择 IBM Bluemix 开发平台的主要原因是： 同时支持运行时（基于 Cloud F [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>现在国内外流行的云平台越来越多，且不谈像国外的 Amazon ECS、AWS 以及 国内的阿里云、BAE、SAE 这些老牌的云服务，目前提供 Docker 容器服务的平台也多起来，国外有 StackEngine、Tutum、IBM Bluemix 等云平台，国内也有 DaoCloud、时速云、灵雀云之类提供 Docker 容器服务的平台。最近对 Docker 也稍微有了点兴趣，比较之下准备拿 IBM Bluemix 来捣鼓 Docker 容器练练手。</p>
<p>本文不打算对 Docker 容器技术本身对什么介绍了，选择 IBM Bluemix 开发平台的主要原因是：</p>
<ul>
<li>同时支持运行时（基于 Cloud Foundry）、容器和虚拟机（基于 openstack）；</li>
<li>服务器在国外，绑定域名不需要备案，这个优势我也挺无奈；</li>
<li>免费账户提供两个公网 IP、每个月有 365 GB-小时的运行内存额度（相当于一个 512MB 内存的容器运行一个月），能基本满足需求；</li>
<li>容器可以开启多个自己指定的 TCP 端口；</li>
<li>IBM 出的货应该还比较稳定。</li>
</ul>
<p>有关 IBM Bluemix 的详情请参考官网：</p>
<p><a href="http://www.ibm.com/cloud-computing/bluemix/cn-zh/">http://www.ibm.com/cloud-computing/bluemix/cn-zh/</a></p>
<p>注册账户什么的这里就不说了，Bluemix 的免费账户有 30 天的试用期，30 天之后添加有效的信用卡就可以继续使用免费额度内的运行时和容器。</p>
<p>Bluemix 提供了 Cloud Foundry 命令行界面 (cf) 来修改应用程序、服务实例和服务绑定，Cloud Foundry CLI 也可以安装 IBM Containers 插件以实现命令行方式管理 Docker 容器。考虑为了熟悉 Docker 命令行的目的，而且 Bluemix 容器中有一些高级功能使用 IBM Containers 插件（cf ic）可以很方便的完成，本文就主要介绍以 IBM Containers 插件（cf ic） CLI 命令的方式管理你的 Bluemix 容器。</p>
<p>IBM Containers 插件 (cf ic) 的官方文档请参考这里：</p>
<p><a href="https://console.ng.bluemix.net/docs/containers/container_cli_cfic.html">https://console.ng.bluemix.net/docs/containers/container_cli_cfic.html</a></p>
<h2 id="安装-cf-ic-插件">安装 cf ic 插件</h2>
<p>cf ic 插件的具体安装步骤可以参考 <a href="https://console.ng.bluemix.net/docs/containers/container_cli_ov.html#container_cli_cfic_install">IBM 官方文档</a>，这里我做个简单的说明。</p>
<p>IBM Containers 插件 (cf ic) 需要依赖 Docker 1.6 以上版本，而 Docker 官方推荐在 Linux 3.8 以上版本的 64 位系统中运行，我使用的 Chromebook 刚好能满足要求，在 Crouton 的 Ubuntu 14.04 环境中安装 Docker 非常简单：</p>
<pre class="brush: bash; title: ; notranslate">
(trusty)zzm@localhost:~$ sudo apt-get install docker.io
</pre>
<p>接着需要从 <a href="https://github.com/cloudfoundry/cli/releases">https://github.com/cloudfoundry/cli/releases</a> 下载安装 Cloud Foundry 命令行界面 (cf)。</p>
<p>按照 IBM 官方文档介绍的使用 cf 命令安装容器插件时遇到报错：</p>
<pre class="brush: bash; title: ; notranslate">
(trusty)zzm@localhost:~$ cf install-plugin https://static-ice.ng.bluemix.net/ibm-containers-linux_x64

**Attention: Plugins are binaries written by potentially untrusted authors. Install and use plugins at your own risk.**

Do you want to install the plugin https://static-ice.ng.bluemix.net/ibm-containers-linux_x64? (y or n)&gt; y

Attempting to download binary file from internet address...
10138400 bytes downloaded...
Installing plugin /tmp/ibm-containers-linux_x64...
FAILED
exit status 2
</pre>
<p>插件已经下载成功，但运行时直接报错，没办法我使用上面的地址手工下载 <code>ibm-containers-linux_x64</code> 之后运行，看具体是什么导致的运行出错：</p>
<pre class="brush: bash; title: ; notranslate">
(trusty)zzm@localhost:~$ ./ibm-containers-linux_x64
panic: Asset i18n/resources/zh_Hans.all.json not found

goroutine 1 [running]:
github.com/ibm-containers/i18n.initWithLocale(0x8899d0, 0x7, 0x8899d0)
        /home/jenkins/workspace/Containers-CCSCLI/containers-dcscli/plugins/src/github.com/ibm-containers/i18n/i18n.go:61 +0x72
github.com/ibm-containers/i18n.Init(0x7fa7674a3558, 0xc20802eaf0, 0x7fa7674a3658, 0xac8a10, 0xe)
        /home/jenkins/workspace/Containers-CCSCLI/containers-dcscli/plugins/src/github.com/ibm-containers/i18n/i18n.go:55 +0x124
github.com/ibm-containers/i18n.init()
        /home/jenkins/workspace/Containers-CCSCLI/containers-dcscli/plugins/src/github.com/ibm-containers/i18n/i18n.go:41 +0x199
main.init()
        /home/jenkins/workspace/Containers-CCSCLI/containers-dcscli/plugins/src/github.com/ibm-containers/wrapper_docker.go:55 +0x78

goroutine 6 [runnable]:
os/signal.loop()
        /usr/local/go/src/os/signal/signal_unix.go:19
created by os/signal.init·1
        /usr/local/go/src/os/signal/signal_unix.go:27 +0x35
</pre>
<p>看起来是加载中文语言信息（我的 Ubuntu 默认语言就是中文）时出错然后直接退出了，试试直接把语言设为英文运行看看：</p>
<pre class="brush: bash; title: ; notranslate">
LANG=en_US.UTF-8 cf install-plugin https://static-ice.ng.bluemix.net/ibm-containers-linux_x64
......
(trusty)zzm@localhost:~$ cf plugins
Listing Installed Plugins...
OK

Plugin Name      Version   Command Name   Command Help   
IBM-Containers   0.8.826   ic             IBM Containers plug-in
</pre>
<p>这下果然就没有报错了，cf ic 插件安装成功，使用 <code>cf plugins</code> 命令可以列出所有安装的 Cloud Foundry 插件。</p>
<h2 id="初步使用-cf-ic-插件">初步使用 cf ic 插件</h2>
<p>首先使用 <code>cf login</code> 命令登录到区域服务器，目前 Bluemix 有美国南部、悉尼、英国这 3 个区域，这里我使用美国南部(其它区域更换 login 后面的地址即可)，输入邮箱和密码登录之后就可以看到区域信息:</p>
<pre class="brush: bash; title: ; notranslate">
(trusty)zzm@localhost:~$ cf login -a https://api.ng.bluemix.net
API endpoint: https://api.ng.bluemix.net

Email&gt; xxx@gmail.com
Password&gt; 
Authenticating...
OK

Targeted org xxx@gmail.com
Targeted space zohead

API endpoint:   https://api.ng.bluemix.net (API version: 2.40.0)
User:           xxx@gmail.com
Org:            xxx@gmail.com
Space:          zohead
</pre>
<p>然后需要初始化 CLI：</p>
<pre class="brush: bash; title: ; notranslate">
(trusty)zzm@localhost:~$ cf ic init
</pre>
<p>初始化完成之后可以运行 <code>cf ic info</code> 获取容器云服务的状态：</p>
<pre class="brush: bash; title: ; notranslate">
(trusty)zzm@localhost:~/bluemix$ cf ic info
Date/Time                : 2016-03-18 03:02:01.808641119 +0800 CST
Debug Mode               : false
Host/URL                 : https://containers-api.ng.bluemix.net
Registry Host            : registry.ng.bluemix.net
Bluemix API Host/URL     : https://api.ng.bluemix.net
Bluemix Org              : xxx@gmail.com(xxx-xxx-xxx-xxx-xxx)
Bluemix Space            : zohead(xxx-xxx-xxx-xxx-xxx)
CLI Version              : 0.8.826
API Version              : 3.0 2028 2016-03-16T19:34:24
Namespace                : zohead
Environment Name         : prod-dal09-vizio3
Containers Limit         : Unlimited
Containers Usage         : 0
Containers Running       : 0
CPU Limit (cores)        : Unlimited
CPU Usage (cores)        : 0
Memory Limit (MB)        : 2048
Memory Usage (MB)        : 0
Floating IPs Limit       : 2
Floating IPs Allocated   : 0
Public IPs Bound         : 0
</pre>
<p>可以看到试用账户的内存使用限制是 2GB，可以使用两个 IP 地址。</p>
<h2 id="使用-cf-ic-构建-docker-容器">使用 cf ic 构建 Docker 容器</h2>
<p>一般情况下你都可以使用 Bluemix 官方提供的 Docker 镜像进行快速构建，由于通常的 Docker 镜像只是为了运行特定的程序或服务设计的会有不少限制，无法满足我要调试运行多个程序的需求，而且为了后续 SSH 登录之类的考虑，我准备使用 <a href="https://github.com/phusion/baseimage-docker">Baseimage-docker</a> 这个比较特殊的 Docker 镜像来构建容器。</p>
<p>首先在当前目录下生成 Dockerfile 文件：</p>
<pre class="brush: bash; title: ; notranslate">
(trusty)zzm@localhost:~/bluemix$ cat &gt; Dockerfile 
# Use phusion/baseimage as base image.
# See https://github.com/phusion/baseimage-docker/blob/master/Changelog.md for a list of version numbers.
FROM phusion/baseimage:0.9.18

EXPOSE 22 80 443 9080 9443

# Use baseimage-docker's init system.
CMD [&quot;/sbin/my_init&quot;]

# ...put your own build instructions here...

# Clean up APT when done.
RUN apt-get clean &amp;&amp; rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
</pre>
<p>Dockerfile 文件简单说明如下：</p>
<ul>
<li>FROM 后面的 <code>phusion/baseimage:0.9.18</code> 表示 Docker 镜像的地址和版本，具体使用哪个版本请参考上面注释里的 Changelog.md 文档；</li>
<li>EXPOSE 表示此 Docker 容器要导出哪些 TCP 端口以使外部能访问到容器。</li>
</ul>
<blockquote>
<p><strong>提示</strong></p>
<p>IBM Bluemix 目前对容器导出的端口有严格的限制，只有常用端口才能导出，官方论坛里列出的可用端口为： <br />
  <em>22,80,443,9080,9443</em></p>
<p>不属于上面列出的其它端口就算通过 Dockerfile 导出也无法访问。</p>
<p>同时需要注意的是使用 Dockerfile 构建容器完成之后就不能再修改导出的端口了，想再换别的端口只能删除旧容器重新构建，因此开始的端口选择需要谨慎。</p>
</blockquote>
<p>总之我的 Dockerfile 文件将可用的 5 个端口都导出了，下面使用 <code>cf ic build</code> 命令构建容器：</p>
<pre class="brush: bash; title: ; notranslate">
(trusty)zzm@localhost:~/bluemix$ cf ic build -t zohead-server:v1 .
</pre>
<p>上面的 <code>zohead-server</code> 为新的容器名字，最后的 <code>.</code> 参数表示 Dockerfile 文件所在的目录（<code>.</code> 即当前目录）。</p>
<p>容器构建完成之后如果没有什么问题就可以用 <code>cf ic run</code> 命令启动新容器了：</p>
<pre class="brush: bash; title: ; notranslate">
cf ic run --name=zohead-server -m 512 -p 22 -p 80 -p 443 -p 9080 -p 9443 registry.ng.bluemix.net/`cf ic namespace get`/zohead-server:v1
</pre>
<p><code>cf ic run</code> 命令的 <code>--name</code> 参数指定容器名字；<code>-m</code> 参数则是为新容器分配的内存大小，为了不超过免费额度我分配了 512MB；<code>-p</code> 参数指定公开用于 TCP 流量的端口,可以指定多个，这里我指定的 5 个端口和 Dockerfile 文件中的一致。</p>
<p>启动容器成功之后等待一段时间就可以使用 <code>cf ic ip list</code> 命令查看 IBM Bluemix 为你的容器分配的公网 IP 地址，公网 IP 地址分配成功之后才可以直接访问容器中的服务：</p>
<pre class="brush: bash; title: ; notranslate">
(trusty)zzm@localhost:~/bluemix$ cf ic ip list
Number of allocated public IP addresses: 1

Listing the IP addresses in this space...
IP Address       Container ID   
134.168.27.107   xxx-xxx-xxx-xxx-xxx
</pre>
<p>上面列出的 IP Address 就是为容器分配的公网 IP 地址，由于我使用的是美国南部区域，分配的公网 IP 地址 ping 值不是特别理想。</p>
<h2 id="访问-docker-容器">访问 Docker 容器</h2>
<p>即使容器没有分配正确的公网 IP 地址，我们也可以使用 <code>cf ic exec</code> 命令直接访问容器执行命令：</p>
<pre class="brush: bash; title: ; notranslate">
(trusty)zzm@localhost:~/bluemix$ cf ic exec zohead-server echo &quot;Hello Bluemix&quot;
Hello Bluemix
</pre>
<p>当然也可以使用 <code>cf ic exec</code> 命令直接开启一个连接到容器的 Bash Shell 终端：</p>
<pre class="brush: bash; title: ; notranslate">
(trusty)zzm@localhost:~/bluemix$ cf ic exec -t -i zohead-server bash -l
</pre>
<p>有了公网 IP 地址之后，你就可以参考 <a href="https://github.com/phusion/baseimage-docker#enabling_ssh">Baseimage-docker开启 SSH</a> 的说明启用 SSH 证书登录了，这里也比较简单我就不做介绍了。</p>
<p>Bluemix Docker 容器能够 SSH 登录之后就比较方便了，Baseimage-docker 镜像里自带了 Python 3 支持，我们可以直接使用 Python 3 运行一个最简单的 Web 服务器验证导出的 TCP 端口能否正常访问：</p>
<pre class="brush: bash; title: ; notranslate">
root@instance-001afe36:/home/wwwroot# python3 -m http.server 80
</pre>
<p>由于 Bluemix 容器用的是 IBM Containers，我们可以用 <code>capsh</code> 命令看看 Bluemix 是否和其它 Docker 容器服务一样存在权限限制：</p>
<pre class="brush: bash; title: ; notranslate">
root@instance-001afe36:~# capsh --print
Current: = cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap+eip
Bounding set =cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
Securebits: 00/0x0/1'b0
 secure-noroot: no (unlocked)
 secure-no-suid-fixup: no (unlocked)
 secure-keep-caps: no (unlocked)
uid=0(root)
gid=0(root)
groups=0(root)
</pre>
<p>看起来限制和常见的 Docker 平台也差不多，没有一些特别的系统管理权限，例如我想测试的基于 tun 驱动的开源 VPN 服务也无法使用了。</p>
<h2 id="监控-bluemix-容器运行状态">监控 Bluemix 容器运行状态</h2>
<p>一番使用下来你会发现参考 Bluemix 官方的文档就可以基本只用 cf ic 命令实现完成创建、构建、运行和管理 Bluemix 容器这一整套操作了。如果你要监控容器的历史运行状态或者要查看账单之类的那可能还是需要访问 Bluemix Web 控制台的，这个是我的 Docker 容器运行时的内存使用情况：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737370583/bluemix-docker-status.png" alt="Bluemix 容器内存使用情况" title="Bluemix 容器内存使用情况"></p>
<p>基本的功能在 Bluemix Web 控制台里也是可以完成的，如果使用中遇到问题可以在 Web 控制台里创建技术支持工单（只是 IBM 的技术支持响应速度似乎很慢的）。</p>
<blockquote>
<p><strong>注意</strong></p>
<p>我在试用 Bluemix 容器的时候发现有一个不太容易被注意到的坑就是容器里使用 <code>free</code> 或者 <code>top</code> 等命令看到的内存非常大（远远超过我分配的 512MB，似乎显示的是实际服务器的总体内存，而且容器里的程序也能分配超过 512MB 的内存，因此容器里实际运行的程序内存占用需要注意不要超过限额。</p>
</blockquote>
<p><br/>
<p>你也可以在 Web 控制台中点击账户图标，然后进入「管理组织」设置，这里可以修改当前区域的运行时和容器使用配额：</p>
<p><img src="http://res.cloudinary.com/digwht2y0/image/upload/v1737370583/bluemix-quota.png" alt="Bluemix 管理区域配额" title="Bluemix 管理区域配额"></p>
<p>经过十几天对 IBM Bluemix Docker 容器的试用来看，虽然 IBM 的服务器不在国内，但 Bluemix 容器还算是比较稳定的，对于一般的开发者来说调试运行一些服务程序也是比较方便的。</p>
<p>一般用户使用这种容器服务最多的用途像跑博客之类的 Web 服务是没什么问题的，不过国内似乎不少人只是用 Bluemix 容器跑 Shadowsocks 代理了（真的算是国内用户的刚性需求了么 -_-#），对此我只能说 IBM Bluemix 目前还是比较良心的，大家且用且珍惜请不要滥用呢，最后祝玩的开心 ～～～。</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/ibm-bluemix-docker/feed/</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
		<item>
		<title>使用libc封装库修改程序绑定端口</title>
		<link>https://zohead.com/archives/libc-bind-wrapper/</link>
		<comments>https://zohead.com/archives/libc-bind-wrapper/#comments</comments>
		<pubDate>Mon, 11 Aug 2014 14:15:01 +0000</pubDate>
		<dc:creator><![CDATA[Uranus Zhou]]></dc:creator>
				<category><![CDATA[技术]]></category>
		<category><![CDATA[网络技术]]></category>
		<category><![CDATA[bind.so]]></category>
		<category><![CDATA[libc]]></category>
		<category><![CDATA[socket]]></category>
		<category><![CDATA[wrapper]]></category>
		<category><![CDATA[端口]]></category>
		<category><![CDATA[绑定]]></category>
		<category><![CDATA[编程]]></category>
		<category><![CDATA[网络]]></category>

		<guid isPermaLink="false">http://zohead.com/?p=764</guid>
		<description><![CDATA[本文同步自（最佳显示效果请点击）：https://zohead.com/archives/libc-bind-wrapper/ 最近在使用一个第三方程序的时候发现程序绑定的 UDP 端口和现有 Linux 系统中的程序有冲突，系统自带的程序又不好修改端口，而第三方程序更没有源码或者配置文件来指定端口。 这种情况下想到可以用 libc 的封装库自己实现 bind 之类的函数来修改端口号，而网上也找到了 Daniel Ryde 类似的实现： http://www.ryde.net/code/bind.c.txt 编译这个 bind 库可以通过环境变量指定绑定的本地 IP 地址，但不支持端口号修改， [&#8230;]]]></description>
				<content:encoded><![CDATA[<p>本文同步自（最佳显示效果请点击）：<a href="https://zohead.com/archives/libc-bind-wrapper/" target="_blank">https://zohead.com/archives/libc-bind-wrapper/</a></p>
<p>最近在使用一个第三方程序的时候发现程序绑定的 UDP 端口和现有 Linux 系统中的程序有冲突，系统自带的程序又不好修改端口，而第三方程序更没有源码或者配置文件来指定端口。</p>
<p>这种情况下想到可以用 libc 的封装库自己实现 bind 之类的函数来修改端口号，而网上也找到了 Daniel Ryde 类似的实现：</p>
<p><a href="http://www.ryde.net/code/bind.c.txt" target="_blank">http://www.ryde.net/code/bind.c.txt</a></p>
<p>编译这个 bind 库可以通过环境变量指定绑定的本地 IP 地址，但不支持端口号修改，而且是直接修改程序所有绑定主机地址不好过滤定制。因此，我对这个 bind 的封装库做了以下改进：</p>
<ul>
<li>支持 socket 类型过滤，可以指定是 TCP、UDP socket 等；</li>
<li>支持指定是本地监听请求还是连接远程的请求；</li>
<li>支持修改本地使用的 IP 地址及端口号；</li>
<li>支持通过环境变量过滤掉某些端口</li>
</ul>
<p>支持上面这些新增的功能之后，我们就可以根据实际情况只修改特定请求的绑定地址或者绑定端口以满足需求。</p>
<p>我修改的 bind 封装库代码如下，也比较简单：</p>
<pre class="brush: cpp; title: bind.c; notranslate">
/*
   Copyright (C) 2000  Daniel Ryde

   Modified by Uranus Zhou (2014)

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
*/
#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;sys/types.h&gt;
#include &lt;sys/socket.h&gt;
#include &lt;netinet/in.h&gt;
#include &lt;dlfcn.h&gt;
#include &lt;errno.h&gt;
#include &lt;string.h&gt;

int (*real_bind)(int, const struct sockaddr *, socklen_t);
int (*real_connect)(int, const struct sockaddr *, socklen_t);

char *bind_addr_env, *connect_bind_addr_env, *bind_port_env, *bind_type_env, *exclude_ports_env, *connect_port_env, *connect_type_env, *connect_bind_port_env;
unsigned long int bind_addr_saddr;
struct sockaddr_in local_sockaddr_in[] = { 0 };
int bind_port_ns = -1, connect_port_ns = -1;
int bind_type = -1, connect_type = -1;

static int get_sock_type(const char *str)
{
	if (strcasecmp(str, &quot;TCP&quot;) == 0)
		return SOCK_STREAM;
	else if (strcasecmp(str, &quot;UDP&quot;) == 0)
		return SOCK_DGRAM;
	else if (strcasecmp(str, &quot;RAW&quot;) == 0)
		return SOCK_RAW;
	else if (strcasecmp(str, &quot;PACKET&quot;) == 0)
		return SOCK_PACKET;
	else
		return -1;
}

void _init (void)
{
	const char *err;

	real_bind = dlsym (RTLD_NEXT, &quot;bind&quot;);
	if ((err = dlerror ()) != NULL) {
		fprintf (stderr, &quot;dlsym (bind): %s\n&quot;, err);
	}

	real_connect = dlsym (RTLD_NEXT, &quot;connect&quot;);
	if ((err = dlerror ()) != NULL) {
		fprintf (stderr, &quot;dlsym (connect): %s\n&quot;, err);
	}

	if (bind_addr_env = getenv (&quot;BIND_ADDR&quot;))
		bind_addr_saddr = inet_addr (bind_addr_env);

	if (bind_port_env = getenv (&quot;BIND_PORT&quot;))
		bind_port_ns = htons (atoi(bind_port_env));

	if (bind_type_env = getenv (&quot;BIND_TYPE&quot;))
		bind_type = get_sock_type(bind_type_env);

	exclude_ports_env = getenv (&quot;EXCLUDE_PORTS&quot;);

	if (connect_port_env = getenv (&quot;CONNECT_PORT&quot;))
		connect_port_ns = htons (atoi(connect_port_env));

	if (connect_type_env = getenv (&quot;CONNECT_TYPE&quot;))
		connect_type = get_sock_type(connect_type_env);

	if (connect_bind_addr_env = getenv (&quot;CONNECT_BIND_ADDR&quot;)) {
		local_sockaddr_in-&gt;sin_family = AF_INET;
		local_sockaddr_in-&gt;sin_addr.s_addr = inet_addr (connect_bind_addr_env);
		local_sockaddr_in-&gt;sin_port = htons (0);
	}

	if (connect_bind_port_env = getenv (&quot;CONNECT_BIND_PORT&quot;))
		local_sockaddr_in-&gt;sin_port = htons (atoi(connect_bind_port_env));
}

static int check_port(int port)
{
	char *tmp = exclude_ports_env, *str = NULL;
	if (tmp == NULL) return 0;

	while (1) {
		char szPort[50] = {0};
		str = strchr(tmp, ',');
		if (str == NULL)
			strncpy(szPort, tmp, sizeof(szPort));
		else
			strncpy(szPort, tmp, str - tmp);
		if (atoi(szPort) == port) return 1;
		if (str == NULL) break;
		tmp = str + 1;
	}

	return 0;
}

int bind (int fd, const struct sockaddr *sk, socklen_t sl)
{
	static struct sockaddr_in *lsk_in;

	lsk_in = (struct sockaddr_in *)sk;
	if (lsk_in-&gt;sin_family == AF_INET || lsk_in-&gt;sin_family == AF_INET6) {
		int type, length = sizeof(int);

		getsockopt(fd, SOL_SOCKET, SO_TYPE, &amp;type, &amp;length);

		if (check_port(ntohs(lsk_in-&gt;sin_port)) == 0 &amp;&amp; (bind_type_env == NULL || bind_type == type)) {
			// change bind address
			if (lsk_in-&gt;sin_addr.s_addr == htonl (INADDR_ANY) &amp;&amp; bind_addr_env)
				lsk_in-&gt;sin_addr.s_addr = bind_addr_saddr;

			// change bind port
			if (bind_port_env)
				lsk_in-&gt;sin_port = bind_port_ns;
		}
	}
	return real_bind (fd, sk, sl);
}

int connect (int fd, const struct sockaddr *sk, socklen_t sl)
{
	static struct sockaddr_in *rsk_in;

	rsk_in = (struct sockaddr_in *)sk;
	if (rsk_in-&gt;sin_family == AF_INET || rsk_in-&gt;sin_family == AF_INET6) {
		int type, length = sizeof(int);

		getsockopt(fd, SOL_SOCKET, SO_TYPE, &amp;type, &amp;length);

		if ((connect_port_env == NULL || connect_port_ns == rsk_in-&gt;sin_port) &amp;&amp; (connect_type_env == NULL || connect_type == type)) {
			// change connect bind address or bind port
			if (connect_bind_addr_env)
				real_bind (fd, (struct sockaddr *)local_sockaddr_in, sizeof (struct sockaddr));
		}
	}
	return real_connect (fd, sk, sl);
}
</pre>
<p>编译方法：</p>
<pre><strong>gcc -nostartfiles -fpic -shared bind.c -o bind.so -ldl -D_GNU_SOURCE</strong></pre>
<p>运行指定程序时需要通过 LD_PRELOAD 预先加载 bind.so 库，并通过环境变量指定如何过滤修改网络参数：</p>
<ul>
<li>BIND_ADDR 指定本地监听的 IP 地址；</li>
<li>BIND_PORT 指定本地监听的端口；</li>
<li>BIND_TYPE 指定本地监听的 socket 类型（TCP、UDP、RAW、PACKET）；</li>
<li>CONNECT_PORT 指定连接远程主机的端口；</li>
<li>CONNECT_TYPE 指定远程主机 socket 连接类型（与 BIND_TYPE 类似）；</li>
<li>CONNECT_BIND_ADDR 指定连接远程主机时本地使用的 IP 地址；</li>
<li>CONNECT_BIND_PORT 指定连接远程主机时本地使用的端口；</li>
<li>EXCLUDE_PORTS 需要排除的端口列表（以逗号隔开）</li>
</ul>
<p>这里举个例子说明如何使用 bind.so：</p>
<p>假设第三方程序为 testapp，testapp 启动时会本地监听 UDP 775 端口，另外还会在本地 eth0 网卡上监听一个随机的 UDP 端口。现在我们需要将后面这个随机的 UDP 端口固定为 666，而且需要指定使用 eth2 网卡，就可以这样运行 testapp 程序：</p>
<pre><strong>BIND_ADDR="eth2-ip" BIND_PORT=666 BIND_TYPE=UDP EXCLUDE_PORTS=775 LD_PRELOAD=bind.so testapp</strong></pre>
<p>上面的命令中指定了本地 IP 地址（eth2-ip）、绑定端口（666）、socket 类型（UDP），并且使用 EXCLUDE_PORTS 环境变量过滤了固定监听的 775 端口，这样 testapp 程序运行使用的随机 UDP 端口就固定在 666 上了。</p>
<p>我修改的 bind.c 源代码可以从我的 Gist 下载：</p>
<p><a href="https://gist.github.com/zohead/9950663ca01952c940eb" target="_blank">https://gist.github.com/zohead/9950663ca01952c940eb</a></p>
<p>实际使用如果有任何问题欢迎提出指正哦，玩的开心 ^_^</p>
]]></content:encoded>
			<wfw:commentRss>https://zohead.com/archives/libc-bind-wrapper/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
