如何在C编写的Nginx模块中获取服务器静态IP?
问题背景
我有一台监听0.0.0.0的Nginx反向代理服务器,server_name为feng.cc:8080,核心配置如下:
upstream ssl-proxy { server 172.16.43.210:5000; } server { listen 0.0.0.0:8080; server_name feng.cc:8080; location / { proxy_pass http://ssl-proxy/; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header Connection ""; } }
服务器静态IP为172.16.39.52,feng.cc是hosts文件中映射到该IP的自定义域名。
我在编写Nginx C模块时,尝试通过ngx_http_request_s结构体的r->connection->local_sockaddr、r->connection->listening等字段获取服务器静态IP,但返回的都是0.0.0.0。不过观察到Nginx与上游服务器通信时确实使用的是静态IP172.16.39.52(tcpdump结果如下):
$ hostname -I 172.16.39.52 $ tcpdump -nn -iany dst port 5000 eth0 Out IP 172.16.39.52.26130 > 172.16.43.210.5000: Flags [S], seq 3565149610, win 32120, options [mss 1460,sackOK,TS val 1811543954 ecr 0,nop,wscale 7], length 0 eth0 Out IP 172.16.39.52.26130 > 172.16.43.210.5000: Flags [.], ack 515140155, win 251, options [nop,nop,TS val 1811543955 ecr 2228136305], length 0 eth0 Out IP 172.16.39.52.26130 > 172.16.43.210.5000: Flags [P.], seq 0:139, ack 1, win 251, options [nop,nop,TS val 1811543955 ecr 2228136305], length 139 eth0 Out IP 172.16.39.52.26130 > 172.16.43.210.5000: Flags [.], ack 174, win 250, options [nop,nop,TS val 1811543957 ecr 2228136307], length 0 eth0 Out IP 172.16.39.52.26130 > 172.16.43.210.5000: Flags [.], ack 203, win 250, options [nop,nop,TS val 1811543957 ecr 2228136307], length 0 eth0 Out IP 172.16.39.52.26130 > 172.16.43.210.5000: Flags [F.], seq 139, ack 203, win 250, options [nop,nop,TS val 1811543958 ecr 2228136307], length 0 eth0 Out IP 172.16.39.52.26130 > 172.16.43.210.5000: Flags [.], ack 204, win 250, options [nop,nop,TS val 1811543959 ecr 2228136309], length 0
请问有没有办法在Nginx C模块中获取服务器的静态IP?
解决方案
方法1:解析本地网卡信息(直接获取系统静态IP)
调用系统API遍历网卡接口,筛选出非回环、处于UP状态的目标网卡IP。适合需要独立于Nginx连接状态获取IP的场景。
示例代码片段:
#include <ifaddrs.h> #include <netinet/in.h> #include <arpa/inet.h> ngx_str_t get_local_static_ip(ngx_pool_t *pool) { ngx_str_t ip = ngx_null_string; struct ifaddrs *ifap, *ifa; struct sockaddr_in *sa; if (getifaddrs(&ifap) != 0) { return ip; } for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { if (ifa->ifa_addr == NULL || ifa->ifa_flags & IFF_LOOPBACK) { continue; } if (ifa->ifa_addr->sa_family == AF_INET) { sa = (struct sockaddr_in *) ifa->ifa_addr; // 按网卡名称过滤,比如目标网卡是eth0 if (ngx_strcmp((u_char *)ifa->ifa_name, (u_char *)"eth0") == 0) { u_char *ip_str = inet_ntoa(sa->sin_addr); ip.len = ngx_strlen(ip_str); ip.data = ngx_palloc(pool, ip.len + 1); if (ip.data == NULL) { break; } ngx_memcpy(ip.data, ip_str, ip.len); ip.data[ip.len] = '\0'; break; } } } freeifaddrs(ifap); return ip; }
方法2:利用上游连接信息获取出口IP
当Nginx与上游建立连接时,会使用系统路由选择的出口IP(即静态IP)。可以在模块中拦截上游连接事件,从上游连接的sockaddr中提取实际通信的IP。
示例思路代码:
// 在处理上游请求的回调逻辑中 if (r->upstream && r->upstream->peer && r->upstream->peer->connection) { ngx_connection_t *upstream_conn = r->upstream->peer->connection; if (upstream_conn->local_sockaddr->sa_family == AF_INET) { struct sockaddr_in *sa = (struct sockaddr_in *) upstream_conn->local_sockaddr; u_char *ip_str = inet_ntoa(sa->sin_addr); // 将ip_str转换为ngx_str_t使用 } }
注意:该方法仅在Nginx与上游已建立连接后生效,适合和上游通信强相关的场景。
方法3:配置文件显式指定,模块读取配置
在Nginx配置中添加自定义配置项,直接指定静态IP,模块读取该配置值。这种方法简单可控,适合静态IP固定的场景。
- 配置文件中添加自定义项:
server { listen 0.0.0.0:8080; server_name feng.cc:8080; # 自定义静态IP配置 local_static_ip 172.16.39.52; location / { # 原有配置... } }
- 模块中实现配置读取:
// 定义配置结构体 typedef struct { ngx_str_t local_static_ip; } ngx_http_my_module_loc_conf_t; // 配置解析回调 static char *ngx_http_my_module_set_static_ip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_my_module_loc_conf_t *mlcf = conf; ngx_str_t *value = cf->args->elts; mlcf->local_static_ip = value[1]; return NGX_CONF_OK; } // 注册配置命令 static ngx_command_t ngx_http_my_module_commands[] = { { ngx_string("local_static_ip"), NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_http_my_module_set_static_ip, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_my_module_loc_conf_t, local_static_ip), NULL }, ngx_null_command }; // 请求处理中获取配置 static ngx_int_t ngx_http_my_module_handler(ngx_http_request_t *r) { ngx_http_my_module_loc_conf_t *mlcf = ngx_http_get_module_loc_conf(r, ngx_http_my_module); ngx_str_t static_ip = mlcf->local_static_ip; // 使用static_ip进行业务逻辑 return NGX_OK; }
内容的提问来源于stack exchange,提问作者hartvon
相关产品推荐
相关产品推荐

