为什么打了fwmark商标注册标记怎么打但却不起作用

新手园地& & & 硬件问题Linux系统管理Linux网络问题Linux环境编程Linux桌面系统国产LinuxBSD& & & BSD文档中心AIX& & & 新手入门& & & AIX文档中心& & & 资源下载& & & Power高级应用& & & IBM存储AS400Solaris& & & Solaris文档中心HP-UX& & & HP文档中心SCO UNIX& & & SCO文档中心互操作专区IRIXTru64 UNIXMac OS X门户网站运维集群和高可用服务器应用监控和防护虚拟化技术架构设计行业应用和管理服务器及硬件技术& & & 服务器资源下载云计算& & & 云计算文档中心& & & 云计算业界& & & 云计算资源下载存储备份& & & 存储文档中心& & & 存储业界& & & 存储资源下载& & & Symantec技术交流区安全技术网络技术& & & 网络技术文档中心C/C++& & & GUI编程& & & Functional编程内核源码& & & 内核问题移动开发& & & 移动开发技术资料ShellPerlJava& & & Java文档中心PHP& & & php文档中心Python& & & Python文档中心RubyCPU与编译器嵌入式开发驱动开发Web开发VoIP开发技术MySQL& & & MySQL文档中心SybaseOraclePostgreSQLDB2Informix数据仓库与数据挖掘NoSQL技术IT业界新闻与评论IT职业生涯& & & 猎头招聘IT图书与评论& & & CU技术图书大系& & & Linux书友会二手交易下载共享Linux文档专区IT培训与认证& & & 培训交流& & & 认证培训清茶斋投资理财运动地带快乐数码摄影& & & 摄影器材& & & 摄影比赛专区IT爱车族旅游天下站务交流版主会议室博客SNS站务交流区CU活动专区& & & Power活动专区& & & 拍卖交流区频道交流区
白手起家, 积分 19, 距离下一级还需 181 积分
论坛徽章:0
先简单说说需求。两个wan口,n个lan口,根据不同的协议进行不同的转发。
我用的是clearos(软路由)和layer7-user-space。
对每个满足设置协议的数据包打标记8。如果标记8则转发到wan1,否则转发到wan2。
之后设置snat,如果标记8,snat的源地址改为wan1的地址。否则同理设置为wan2的。
感觉这样设置没问题了。但是出现了下面的情况。
QQ图片48.jpg (19.84 KB, 下载次数: 3)
18:12 上传
上面的policy ACCEPT接受0个包,为什么下面的规则还有处理的数据包?
下面附上我的设置。&&我的layer7给QQ协议打标记8,经测试,这步肯定没问题。
我的环境是用虚拟机搭建的。wan1和wan2其实都是局域网内分配的,网管是192.168.72.254
我执行了下面的脚本
#! /bin/sh
ip route add table 8 via 192.168.72.254 dev eth1 #这个是wan2,对应的IP是192.168.72.106
ip route add table 10 via 192.168.72.254 dev eth0 #这个是wan1,对应的IP是192.168.72.103
iptables -F
iptables -t mangle -A PREROUTING -s 192.168.164.0/24 -j NFQUEUE
iptables -t mangle -A PREROUTING -d 192.168.164.0/24 -j NFQUEUE #这两步是为了layer在用户态可以执行,这步设置没问题
iptables -t nat -A POSTROUTING -m mark ! --mark 8 -j SNAT --to-source 192.168.72.103
iptables -t nat -A POSTROUTING -m mark --mark 8 -j SNAT --to-source 192.168.72.106
跟踪各个规则,发现在POSTROUTING的mangle点的hook函数中,还可以检测到被标记的数据包。
到下一个hook函数,也就是nat表中的函数,就检测不到了。
出现了上面图片的结果。
这什么情况啊?完全没头绪
&&nbsp|&&nbsp&&nbsp|&&nbsp&&nbsp|&&nbsp&&nbsp|&&nbsp
论坛徽章:379
你现在数据包不是能发送到对应的wan口吗.
你试试在nat的时候,不用mark,直接-o ethx -j SNAT ...
这样能不能满足要求
白手起家, 积分 19, 距离下一级还需 181 积分
论坛徽章:0
还是不可以啊。只能转发到第一个eth0。会不会是通过fwmark来识别不同的路由表的时候出了问题?回复
论坛徽章:379
不清楚,你先保证数据包按照你的要求被转发到相应的端口.
白手起家, 积分 19, 距离下一级还需 181 积分
论坛徽章:0
在POSTROUTING上先执行mangle挂载的hook函数,然后是nat挂载的hook函数。
在mangle的hook点,QQ协议数据包发往eth1,但是到了nat,发往eth0的数据包就成了0。好奇怪
论坛徽章:379
我记得策略路由应该可以根据你的数据包的mark选择路由表吧,你测试下
白手起家, 积分 19, 距离下一级还需 181 积分
论坛徽章:0
使用fwmark可以根据标记选择路由表
从结果看,应该是已经选择了正确的路由表,QQ协议数据包确实发往eth1。但是在POSTROUINT的第二个hook函数中,发往eth1的数据包没了
家境小康, 积分 1079, 距离下一级还需 921 积分
论坛徽章:0
学习学习了android支持多种网络类型(WAN口),例如WIFI、3G等。目前android的实现是,WIFI和3G只能同时存在一个(优先级),例如当WIFI连接后,数据通路就从3G切换到WIFI。对上层app而言,这时候数据通路也就从3G切换到WIFI上。
考虑一个特殊的需求,某app只能通过WIFI接口去传输数据,是否可以实现?较新版本的android已经支持了该功能,通过调用setProcessDefaultNetwork()可以指定某一进程的网络接口,
* Binds the current process to {@code network}.
All Sockets created in the future
* (and not explicitly bound via a bound SocketFactory from
* {@link Network#getSocketFactory() Network.getSocketFactory()}) will be bound to
* {@code network}.
All host name resolutions will be limited to {@code network} as well.
* Note that if {@code network} ever disconnects, all Sockets created in this way will cease to
* work and all host name resolutions will fail.
This is by design so an application doesn't
* accidentally use Sockets it thinks are still bound to a particular {@link Network}.
* To clear binding pass {@code null} for {@code network}.
Using individually bound
* Sockets created by Network.getSocketFactory().createSocket() and
* performing network-specific host name resolutions via
* {@link Network#getAllByName Network.getAllByName} is preferred to calling
* {@code setProcessDefaultNetwork}.
* network The {@link Network} to bind the current process to, or {@code null} to clear
the current binding.
* {@code true} on success, {@code false} if the {@link Network} is no longer valid.
public static boolean setProcessDefaultNetwork(Network network) {
该函数的实现原理大致为,
1. 该进程在创建socket时(app首先调用setProcessDefaultNetwork()),android底层会利用setsockopt函数设置该socket的SO_MARK为netId(android有自己的管理逻辑,每个Network有对应的ID),以后利用该socket发送的数据都会被打上netId的标记(fwmark 值)。
2. 利用策略路由,将打着netId标记的数据包都路由到WIFI的接口wlan0。
这里先介绍打标签的原理,至于策略路由的创建,后续再分析,下面是策略路由表的一个简单例子。
shell@msm8916_64:/ $ ip rule list
ip rule list
from all lookup local
from all fwmark 0xc0000/0xd0000 lookup 99
from all fwmark 0x10063/0x1ffff lookup 97
from all fwmark 0x10064/0x1ffff lookup 1012
from all oif rmnet_data0 lookup 1012
from all fwmark 0x0/0x10000 lookup 99
from all fwmark 0x0/0x10000 lookup 98
from all fwmark 0x0/0x10000 lookup 97
from all fwmark 0x64/0x1ffff lookup 1012
from all fwmark 0x0/0xffff lookup 1012
from all fwmark 0x0/0xffff uidrange 0-0 lookup main
from all unreachable
shell@msm8916_64:/ $
java层利用JNI和C库进行交互
android java库是libcore\目录下的相关代码,而上层的java api基本都是调用该java库中的实现,然后java库通过JNI的方式调用底层的实现,例如android中的c库bionic\等。
下面,我们从java层调用Socket函数开始,分析上述调用的过程。
public Socket(String dstName, int dstPort, InetAddress localAddress, int localPort) throws IOException {
tryAllAddresses(dstName, dstPort, localAddress, localPort, true);
public Socket() {
this.impl = factory != null ? factory.createSocketImpl() : new PlainSocketImpl();
this.proxy = null;
private void tryAllAddresses(String dstName, int dstPort, InetAddress
localAddress, int localPort, boolean streaming) throws IOException {
startupSocket(dstAddress, dstPort, localAddress, localPort, streaming);
private void startupSocket(InetAddress dstAddress, int dstPort,
InetAddress localAddress, int localPort, boolean streaming)
throws IOException {
synchronized (this) {
impl.create(streaming);
impl.connect(dstAddress, dstPort);
protected void create(boolean streaming) throws IOException {
this.streaming =
this.fd = IoBridge.socket(streaming);
public static FileDescriptor socket(boolean stream) throws SocketException {
fd = Libcore.os.socket(AF_INET6, stream ? SOCK_STREAM : SOCK_DGRAM, 0);
} catch (ErrnoException errnoException) {
throw errnoException.rethrowAsSocketException();
package libcore.
public final class Libcore {
private Libcore() { }
public static Os os = new BlockGuardOs(new Posix());
public interface Os {
其实最终调用的是类Posix中的方法,下面是Posix中的socket()函数,是个native函数,
public native FileDescriptor socket(int domain, int type, int protocol) throws ErrnoE
其实现为,
static jobject Posix_socket(JNIEnv* env, jobject, jint domain, jint type, jint protocol) {
int fd = throwIfMinusOne(env, "socket", TEMP_FAILURE_RETRY(socket(domain, type, protocol)));
return fd != -1 ? jniCreateFileDescriptor(env, fd) : NULL;
int socket(int domain, int type, int protocol) {
return __netdClientDispatch.socket(domain, type, protocol);
bonic库中socket相关的实现
在bonic库中,__netdClientDispatch是什么?
struct NetdClientDispatch {
int (*accept4)(int, struct sockaddr*, socklen_t*, int);
int (*connect)(int, const struct sockaddr*, socklen_t);
int (*socket)(int, int, int);
unsigned (*netIdForResolv)(unsigned);
extern __LIBC_HIDDEN__ struct NetdClientDispatch __netdClientD
extern "C" __socketcall int __accept4(int, sockaddr*, socklen_t*, int);
extern "C" __socketcall int __connect(int, const sockaddr*, socklen_t);
extern "C" __socketcall int __socket(int, int, int);
static unsigned fallBackNetIdForResolv(unsigned netId) {
return netId;
__LIBC_HIDDEN__ NetdClientDispatch __netdClientDispatch __attribute__((aligned(32))) = {
__accept4,
__connect,
fallBackNetIdForResolv,
到此,我们分析了__netdClientDispatch是什么。
此外,bonic在preinit时会调用netdClientInit(),分析下发生了什么,
__attribute__((constructor)) static void __libc_preinit() {
netdClientInit();
extern "C" __LIBC_HIDDEN__ void netdClientInit() {
if (pthread_once(&netdClientInitOnce, netdClientInitImpl)) {
__libc_format_log(ANDROID_LOG_ERROR, "netdClient", "Failed to initialize netd_client");
static void netdClientInitImpl() {
void* netdClientHandle = dlopen("libnetd_client.so", RTLD_LAZY);
if (netdClientHandle == NULL) {
netdClientInitFunction(netdClientHandle, "netdClientInitAccept4",
&__netdClientDispatch.accept4);
netdClientInitFunction(netdClientHandle, "netdClientInitConnect",
&__netdClientDispatch.connect);
netdClientInitFunction(netdClientHandle, "netdClientInitNetIdForResolv",
&__netdClientDispatch.netIdForResolv);
netdClientInitFunction(netdClientHandle, "netdClientInitSocket", &__netdClientDispatch.socket);
template &typename FunctionType&
static void netdClientInitFunction(void* handle, const char* symbol, FunctionType* function) {
typedef void (*InitFunctionType)(FunctionType*);
InitFunctionType initFunction = reinterpret_cast&InitFunctionType&(dlsym(handle, symbol));
if (initFunction != NULL) {
initFunction(function);
而库libnetd_client.so是在netd中,system\netd\client\。
typedef int (*SocketFunctionType)(int, int, int);
SocketFunctionType libcSocket = 0;
extern "C" void netdClientInitSocket(SocketFunctionType* function) {
if (function && *function) {
libcSocket = *function;
*function = netdClientS
所以在bonic在初始化时,主要做的工作就是将__netdClientDispatch.socket函数设置为netdClientSocket(),而libcSocket 赋值为__socket()(socket相关函数的实现都类似)。
setProcessDefaultNetwork()的实现
public static boolean setProcessDefaultNetwork(Network network) {
int netId = (network == null) ? NETID_UNSET : network.netId;
if (netId == NetworkUtils.getNetworkBoundToProcess()) {
return true;
if (NetworkUtils.bindProcessToNetwork(netId)) {
return true;
return false;
bindProcessToNetwork()利用JNI实现,
* Binds the current process to the network designated by {@code netId}.
All sockets created
* in the future (and not explicitly bound via a bound {@link SocketFactory} (see
* {@link Network
* {@code Network} ever disconnects all sockets created in this way will cease to work.
* is by design so an application doesn't accidentally use sockets it thinks are still bound to
* a particular {@code Network}.
Passing NETID_UNSET clears the binding.
public native static boolean bindProcessToNetwork(int netId);
static jboolean android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId)
return (jboolean) !setNetworkForProcess(netId);
std::atomic_uint netIdForProcess(NETID_UNSET);
extern "C" int setNetworkForProcess(unsigned netId) {
return setNetworkForTarget(netId, &netIdForProcess);
int setNetworkForTarget(unsigned netId, std::atomic_uint* target) {
if (netId == NETID_UNSET) {
*target = netId;
// Verify that we are allowed to use |netId|, by creating a socket and trying to have it marked
// with the netId. Call libcSocket() else the socket creation (via netdClientSocket())
// might itself cause another check with the fwmark server, which would be wasteful.
int socketFd;
//libcSocket 赋值为__socket
if (libcSocket) {
socketFd = libcSocket(AF_INET6, SOCK_DGRAM, 0);
socketFd = socket(AF_INET6, SOCK_DGRAM, 0);
if (socketFd & 0) {
int error = setNetworkForSocket(netId, socketFd);
if (!error) {
//将netIdForProcess设置为netId
*target = netId;
close(socketFd);
extern "C" int setNetworkForSocket(unsigned netId, int socketFd) {
if (socketFd & 0) {
return -EBADF;
FwmarkCommand command = {FwmarkCommand::SELECT_NETWORK, netId, 0};
return FwmarkClient().send(&command, sizeof(command), socketFd);
int FwmarkClient::send(void* data, size_t len, int fd) {
mChannel = socket(AF_UNIX, SOCK_STREAM, 0);
if (mChannel == -1) {
if (TEMP_FAILURE_RETRY(connect(mChannel, reinterpret_cast&const sockaddr*&(&FWMARK_SERVER_PATH),
sizeof(FWMARK_SERVER_PATH))) == -1) {
iov.iov_base =
iov.iov_len =
memset(&message, 0, sizeof(message));
message.msg_iov = &
message.msg_iovlen = 1;
char cmsg[CMSG_SPACE(sizeof(fd))];
memset(cmsgu.cmsg, 0, sizeof(cmsgu.cmsg));
message.msg_control = cmsgu.
message.msg_controllen = sizeof(cmsgu.cmsg);
cmsghdr* const cmsgh = CMSG_FIRSTHDR(&message);
cmsgh-&cmsg_len = CMSG_LEN(sizeof(fd));
cmsgh-&cmsg_level = SOL_SOCKET;
cmsgh-&cmsg_type = SCM_RIGHTS;
memcpy(CMSG_DATA(cmsgh), &fd, sizeof(fd));
if (TEMP_FAILURE_RETRY(sendmsg(mChannel, &message, 0)) == -1) {
int error = 0;
if (TEMP_FAILURE_RETRY(recv(mChannel, &error, sizeof(error), 0)) == -1) {
netd中的fwmarkd收到数据,
bool FwmarkServer::onDataAvailable(SocketClient* client) {
int socketFd = -1;
int error = processClient(client, &socketFd);
if (socketFd &= 0) {
close(socketFd);
return false;
int FwmarkServer::processClient(SocketClient* client, int* socketFd) {
iov.iov_base = &
iov.iov_len = sizeof(command);
memset(&message, 0, sizeof(message));
message.msg_iov = &
message.msg_iovlen = 1;
char cmsg[CMSG_SPACE(sizeof(*socketFd))];
memset(cmsgu.cmsg, 0, sizeof(cmsgu.cmsg));
message.msg_control = cmsgu.
message.msg_controllen = sizeof(cmsgu.cmsg);
int messageLength = TEMP_FAILURE_RETRY(recvmsg(client-&getSocket(), &message, 0));
if (messageLength &= 0) {
if (messageLength != sizeof(command)) {
return -EBADMSG;
cmsghdr* const cmsgh = CMSG_FIRSTHDR(&message);
if (cmsgh && cmsgh-&cmsg_level == SOL_SOCKET && cmsgh-&cmsg_type == SCM_RIGHTS &&
cmsgh-&cmsg_len == CMSG_LEN(sizeof(*socketFd))) {
memcpy(socketFd, CMSG_DATA(cmsgh), sizeof(*socketFd));
if (*socketFd & 0) {
return -EBADF;
socklen_t fwmarkLen = sizeof(fwmark.intValue);
if (getsockopt(*socketFd, SOL_SOCKET, SO_MARK, &fwmark.intValue, &fwmarkLen) == -1) {
Permission permission = mNetworkController-&getPermissionForUser(client-&getUid());
switch (command.cmdId) {
case FwmarkCommand::SELECT_NETWORK: {
fwmark.netId = command.netId;
if (command.netId == NETID_UNSET) {
fwmark.explicitlySelected = false;
fwmark.protectedFromVpn = false;
permission = PERMISSION_NONE;
if (int ret = mNetworkController-&checkUserNetworkAccess(client-&getUid(),
command.netId)) {
fwmark.explicitlySelected = true;
fwmark.protectedFromVpn = mNetworkController-&canProtect(client-&getUid());
fwmark.permission =
if (setsockopt(*socketFd, SOL_SOCKET, SO_MARK, &fwmark.intValue,
sizeof(fwmark.intValue)) == -1) {
通过上面分析,setProcessDefaultNetwork()函数仅仅是将进程的netIdForProcess设置为netId。在调用setProcessDefaultNetwork()后,再继续创建socket,connect会做什么?
上面分析在java层调用Socket函数时,最终会调用__netdClientDispatch.socket(),而__netdClientDispatch.socket()被赋值为netdClientSocket,在此步骤中会取出setProcessDefaultNetwork()函数设置的netIdForProcess值,然后设置给socket为其fwmark。
int socket(int domain, int type, int protocol) {
return __netdClientDispatch.socket(domain, type, protocol);
int netdClientSocket(int domain, int type, int protocol) {
int socketFd = -1;
if (propSocket) {
socketFd = propSocket(domain, type, protocol);
} else if (libcSocket) {
socketFd = libcSocket(domain, type, protocol);
if (-1 == socketFd) {
return -1;
unsigned netId = netIdForP
if (netId != NETID_UNSET && FwmarkClient::shouldSetFwmark(domain)) {
if (int error = setNetworkForSocket(netId, socketFd)) {
return closeFdAndSetErrno(socketFd, error);
return socketFd;
关于setNetworkForSocket()实现,上面已经分析过,主要就是为该socketFd通过setsockopt()设置socket的SO_MARK为netId,关于connect(),accept()的实现和socket()类似。
本文已收录于以下专栏:
相关文章推荐
Android5.0之后,网络framework出现很大的变化,原生支持了以太网,并且支持多个网络同时连接同时存在。
其实多网络共存并不是什么特别的事情,大部分机器(windows, linux)都支...
/article/mobile/3762.html
Android5.0网络之多网络共存与应用
Android5.0之后,网络framework出...
程序员升职加薪指南!还缺一个“证”!
CSDN出品策划程序员9月规划,专为码农的升职加薪保驾护航,程序员的捷径,你get到了吗?听说阅读了这篇文章的人,都已实现了小梦想~快来揭秘!
第一部分 Android网络基础
   Android平台浏览器采用了WeBKit引擎,这款名为Chorme Lite的Web浏览器拥有强大扩展特性,每个开发者都以为编写自己的插件,使得浏览器的功能...
WIFI就是一种无线联网技术,常见的是使用无线路由器。那么在这个无线路由器的信号覆盖的范围内都可以采用WIFI连接的方式进行联网。如果无线路由器连接了一个ADSL线路或其他的联网线路,则又被称为“热点...
(OK) Problem With Android Configuration - routing table
在Android 4G ppp拨号之后,能成功获取Ip地址,但是网络却没有跑通。这里涉及到Linu策略路由方面的知识。
第一部分 Android网络基础
   Android平台浏览器采用了WeBKit引擎,这款名为Chorme Lite的Web浏览器拥有强大扩展特性,每个开发者都以为编写自己的插件,使得浏览器的功能更...
android较新的版本中,已经加入了以太网功能,用户可在设置界面设置以太网的连接方式,如DHCP/静态IP/PPPOE。
显然,android的实现中默认以太网是作为一个出去的端口(相当于路由器的W...
现在android设备的种类增多,很多设备都有了以太网的接口。都需要通过应用去设置网络ip 等等。并没有这样的api,
所以可以采取以下方法。
1.让设备获取root权限;
2.由于以下方式,设备关机...
---------------------
从Android M开始,Google就正式推出了官方的权限管理机制Android
Runtime Permission. 
 AppOps...
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)新手园地& & & 硬件问题Linux系统管理Linux网络问题Linux环境编程Linux桌面系统国产LinuxBSD& & & BSD文档中心AIX& & & 新手入门& & & AIX文档中心& & & 资源下载& & & Power高级应用& & & IBM存储AS400Solaris& & & Solaris文档中心HP-UX& & & HP文档中心SCO UNIX& & & SCO文档中心互操作专区IRIXTru64 UNIXMac OS X门户网站运维集群和高可用服务器应用监控和防护虚拟化技术架构设计行业应用和管理服务器及硬件技术& & & 服务器资源下载云计算& & & 云计算文档中心& & & 云计算业界& & & 云计算资源下载存储备份& & & 存储文档中心& & & 存储业界& & & 存储资源下载& & & Symantec技术交流区安全技术网络技术& & & 网络技术文档中心C/C++& & & GUI编程& & & Functional编程内核源码& & & 内核问题移动开发& & & 移动开发技术资料ShellPerlJava& & & Java文档中心PHP& & & php文档中心Python& & & Python文档中心RubyCPU与编译器嵌入式开发驱动开发Web开发VoIP开发技术MySQL& & & MySQL文档中心SybaseOraclePostgreSQLDB2Informix数据仓库与数据挖掘NoSQL技术IT业界新闻与评论IT职业生涯& & & 猎头招聘IT图书与评论& & & CU技术图书大系& & & Linux书友会二手交易下载共享Linux文档专区IT培训与认证& & & 培训交流& & & 认证培训清茶斋投资理财运动地带快乐数码摄影& & & 摄影器材& & & 摄影比赛专区IT爱车族旅游天下站务交流版主会议室博客SNS站务交流区CU活动专区& & & Power活动专区& & & 拍卖交流区频道交流区
家境小康, 积分 1841, 距离下一级还需 159 积分
论坛徽章:1
本帖最后由 jiufei19 于
10:05 编辑
大家好,下面一个问题我一直没有找到错误出在哪里,请大家帮忙指正。
问题描述:
我希望某台linux主机允许来自任意客户端的ssh访问,而不是特定IP发来的ssh访问请求。换句话讲,ssh客户端的ip随时可能发生变化。下面是我的部分配置:
iptables -t mangle -A OUTPUT -p tcp --sport 22 -j MARK --set-mark 100
ip route add default via x.x.x.x table 300
ip rule add fwmark 100 table 300 pref 32759
该设置的思想是对凡是由该linux产生的对ssh客户端的应答都标记其fwmark为100,然后对此标记fwmark为100的数据设置策略路由为查路由表300,而路由表300中缺省路由为x.x.x.x。另外,之所以我要这样设计的原因是主路由表有其他设置,我不能使用主路由表的default来解决,于是必须使用策略,但是经过上述配置后,客户端将无法再访问linux了,说明路由出了问题,fwmark没有起到应有的作用。
请问大家,上面的配置哪里有问题?谢谢!
&&nbsp|&&nbsp&&nbsp|&&nbsp&&nbsp|&&nbsp&&nbsp|&&nbsp
富甲一方, 积分 49404, 距离下一级还需 596 积分
论坛徽章:31
tracert 一下看看 数据包是怎么走的.
家境小康, 积分 1841, 距离下一级还需 159 积分
论坛徽章:1
本帖最后由 jiufei19 于
16:11 编辑
q1208c 发表于
tracert 一下看看 数据包是怎么走的.
请问q1208c,我没有反应过来如何使用tracert来看数据包如何走呢?
下面是我在linux上执行traceroute时的结果,其中192.168.0.145是ssh客户端的当前ip,可见ssh的应答先走192.168.23.2这个网关,然后再到192.168.0.145。但是我在策略路由中也设置了表300的网关为192.168.23.2,按理没有问题啊,但是实际结果不正确只能是说明fwmark没有起作用。
[admin@fedora8 ~]$ traceroute 192.168.0.145
traceroute to 192.168.0.145 (192.168.0.145), 30 hops max, 40 byte packets
1&&192.168.23.2 (192.168.23.2)&&0.487 ms&&0.231 ms&&0.262 ms
2&&192.168.0.145 (192.168.0.145)&&15.292 ms&&14.306 ms&&13.539 ms
家境小康, 积分 1841, 距离下一级还需 159 积分
论坛徽章:1
本帖最后由 jiufei19 于
16:14 编辑
我下面把设置后的策略和路由列出,便于大家帮我发现问题
[admin@fedora8 ~]$ ip rule list
0:& & & && && && &from all lookup local
32759:& & & & from all fwmark 0x64 lookup 300
32766:& & & & from all lookup main
32767:& & & & from all lookup default
[admin@fedora8 ~]$ route
Kernel IP routing table
Destination& &&&Gateway& && && &Genmask& && && &Flags Metric Ref& & Use Iface
192.168.23.0& & *& && && && && && & 255.255.255.0& &U& && &&&0& && &0& && &&&0& &eth1
link-local& && && && &*& && && && && && & 255.255.0.0& && & U& && &&&0& && &0& && &&&0& &eth1
default& && && &&&192.168.23.2& & 0.0.0.0& && && && &UG& && &&&0& && &0& && &&&0& &eth1
[admin@fedora8 ~]$ ip route show table 300
default via 192.168.23.2 dev eth1
此时,ssh客户端还可以访问linux,但是如果我将上面的红色字体的策略去掉,则将立刻无法再连接linux服务器了,因此显然是fwmark没有起作用,正是因为其没有起作用,所以之前红色字体的策略没有删除时,ssh请求的应答没有匹配策略32759,于是按主路由表进行操作,因此保证ssh客户端可以继续访问linux,而一旦去掉红色字体策略,而32759又不匹配,于是就出现了ssh客户端无法再访问linux的现象,为了进一步说明表300没有错误,我故意按如下策略进行了重复设置
[admin@fedora8 ~]$ ip rule list
0:& & & & from all lookup local
32759:& & & & from all fwmark 0x64 lookup 300
32766:& & & & from all lookup 300
32767:& & & & from all lookup default
此时ssh客户端仍然可以正常访问linux,这说明表300的设置是正确的,进一步说明了fwmark没有起作用。为什么会这样呢?一直对fwmark的使用不清晰。
富甲一方, 积分 49404, 距离下一级还需 596 积分
论坛徽章:31
由于你指定了 端口22, 所以, 我建议你使用 tcp traceroute 看看会不会按你的路由来走.
富足长乐, 积分 7969, 距离下一级还需 31 积分
论坛徽章:12
本帖最后由 phanx 于
23:52 编辑
& & 你应该在PREROUTING的时候做set-mark。iptables -t mangle -A PREROUTING -p tcp --sport 22 -j MARK --set-mark 100复制代码
更正:对于本机发出的报文,PREROUTING没有效果。
家境小康, 积分 1841, 距离下一级还需 159 积分
论坛徽章:1
q1208c 发表于
由于你指定了 端口22, 所以, 我建议你使用 tcp traceroute 看看会不会按你的路由来走.
不好意思,我没有理解q1208c的说明呢,q1208c看了我上面两个说明了吗?
另外,当我按fwmark设置后,在linux主机上面直接执行traceroute 192.168.0.145时,将看到“Network is unreachable”的提示信息
家境小康, 积分 1841, 距离下一级还需 159 积分
论坛徽章:1
phanx 发表于
回复 4# jiufei19
为啥不能在OUTPUT这个chain做?我的本意就是要在linux的ssh服务发回应答的时候被标记为fwmark=100,以便后面路由处理。
此外,我也按phanx说的改在PREROUTING做了,可结果仍然不正确。
富甲一方, 积分 49404, 距离下一级还需 596 积分
论坛徽章:31
我看到你的说明了.
所以, 只有 tcp traceroute 才能触发你的 规则被mark上.
普通的traceroute使用的是 icmp协议, 你那规则用不上的.
家境小康, 积分 1841, 距离下一级还需 159 积分
论坛徽章:1
& & 仔细看了下源码,发现主机的外出报文都是从ip_queue_xmit这个函数发出的,而这个函数中是首先进行路由,然后再调用NF_HOOK(NF_IP_LOCAL_OUT),也就是OUTPUT链,这样的话,之前我的策略配置就永远不会有效果了。所以,对于外出报文,可能fwmark的确无法起作用,而如果是转发报文,那么fwmark就可以起作用了。}

我要回帖

更多关于 word每次打开都有标记 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信