目录?
网络数据包过滤的实现
iptables规则解析
如何处理回环地址
工作在三层的Iptables如何实现对MAC的过滤
车联网安全实践
功能性应用
安全性应用
策略配置技巧
参考文章
车联网是防火墙上在C端少有的应用领域。防火墙的经典功能是实现内外网络的隔离,因此在企业内网、服务器网络等面向企业用户的领域内,防火墙有着广泛的应用。而在PC互联网时代,个人主机因其单一的网络结构基本不会单独配置防火墙应用,除非其安装的安全软件包含了防火墙功能。过渡到移动互联网时代之后,用户使用的智能终端由于使用了运营商级别的NAT,因此终端在IPV4网络上不能够被直接连接,再加上终端系统对权限的严格管控,使得防火墙在个人用户智能终端上显得愈发不那么重要。但进入到车联网时代和IOT时代之后,网络结构重新变得复杂起来,比如车内各种域控制器、传感器、网联模块等,组成了复杂的车内网络,承载着异常复杂的业务,防火墙的隔离功能再次变得重要起来。
网络数据包过滤的实现
Iptables防火墙包含用户空间的Iptables规则管理工具和内核空间的Netfilter包过滤模块。其中,Netfilter实现的包过滤为上层的iptables规则提供了强大和丰富的过滤逻辑。
Netfilter在内核的5个地方设置了HOOK 点,用户可以通过配置 iptables 规则,在HOOK点对报文进行过滤、修改等操作。这五个过滤点在包传输路径上的逻辑位置如下图所示:
接收路径
网卡收到报文,通过网卡驱动发送给内核协议栈,当报文达到协议栈的网络层(IP层)时,需要确定报文是应该继续往上层协议栈传送(local deliver),还是通过转发信息表转发(forward)出去;
发送路径
对本机向外发送(local out)的报文,经上层协议栈传递至协议栈的IP层,需要确定是从哪个网卡发送出去。
?明确五个HOOK点的位置,是理解iptables规则逻辑的最佳方式。五个HOOK点在协议栈中的具体位置如下图所示:
iptables规则解析
Netfilter的五个HOOK点提供了以规则链(rule chain)的方式管理用户对网络包的处理需求,用户通过iptables工具向规则链添加或删除规则。规则链根据HOOK点或协议栈位置提供对应的包处理功能,而表(table)则是按照功能对规则进行逻辑分类,如用于过滤的filter表,用于网络地址转换的nat表,表与链的组织关系如下表所示:
表格来源:Things_You_Should_Know_About_Netfilter[1]
?Netfilter在处理每个规则链时,会按照“raw、mangle、nat、filter、security、rawpost”的顺序依次处理对应的规则。
如何处理回环地址
在一般的认识中,当本机将数据包发送到本机上指定的一个网卡地址时,该数据包会到达这个指定的网络接口。但事实却不是这样,当数据包寻址后发现目的机器即为发送机器,它会被发送到环回接口。比如下面这个例子[1]:
# ip -4 addr ls eth0
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
inet 192.168.1.21/24 brd 192.168.1.255 scope global eth0
# ip route get 192.168.1.21
local 192.168.1.21 dev lo src 192.168.1.21
cache <local> mtu 16436 advmss 16396 hoplimit 64
我们可以看到,发送到这台机器eth0地址的数据包将在 lo 设备或环回接口上路由,即使目标地址不是 127.0.0.1。
考虑到每个控制器基本都存在域内通信的需求,因此,针对回环地址,我们建议的防火墙策略是应该在任何情况下都要保证其联通性。
工作在三层的Iptables如何实现对MAC的过滤
笔者曾经有一个疑问,因为iptables网络防火墙工作的层级是三层,但是它却能够对处于二层的MAC地址进行过滤,比如下面这个规则:
iptables -A INPUT -m mac --mac-source XX:XX:XX:XX:XX:XX -j DROP
理论上,二层的MAC地址对工作在三层的功能模块是不可见的,但iptables确实实现了对MAC地址的过滤,即iptables在三层获取到了二层的MAC地址,这是怎么实现的呢?
有一种合理的思路是通过查询ARP表获取MAC地址,这种思路在原理上是行得通的。为了验证iptables是否采用这种方式实现,笔者对iptables进行了源码级别的分析,发现事实并非如此。iptables拿到MAC地址的关键,是使用了sk_buff结构。iptables通过sk_buff携带的mac_header等链路层数据结构,穿越了协议栈。sk_buff结构体定义如下:
//include/linux/skbuff.h
/**
* struct sk_buff - socket buffer
* @next: Next buffer in list
* @prev: Previous buffer in list
* @tstamp: Time we arrived/left
* @rbnode: RB tree node, alternative to next/prev for netem/tcp
* @sk: Socket we are owned by
* @dev: Device we arrived on/are leaving by
* @cb: Control buffer. Free for use by every layer. Put private vars here
* @_skb_refdst: destination entry (with norefcount bit)
* @sp: the security path, used for xfrm
* @len: Length of actual data
* @data_len: Data length
* @mac_len: Length of link layer header
* @hdr_len: writable header length of cloned skb
* @csum: Checksum (must include start/offset pair)
* @csum_start: Offset from skb->head where checksumming should start
* @csum_offset: Offset from csum_start where checksum should be stored
* @priority: Packet queueing priority
* @ignore_df: allow local fragmentation
* @cloned: Head may be cloned (check refcnt to be sure)
* @ip_summed: Driver fed us an IP checksum
* @nohdr: Payload reference only, must not modify header
* @nfctinfo: Relationship of this skb to the connection
* @pkt_type: Packet class
* @fclone: skbuff clone status
* @ipvs_property: skbuff is owned by ipvs
* @peeked: this packet has been seen already, so stats have been
* done for it, don't do them again
* @nf_trace: netfilter packet trace flag
* @protocol: Packet protocol from driver
* @destructor: Destruct function
* @nfct: Associated connection, if any
* @nf_bridge: Saved data about a bridged frame - see br_netfilter.c
* @skb_iif: ifindex of device we arrived on
* @tc_index: Traffic control index
* @tc_verd: traffic control verdict
* @hash: the packet hash
* @queue_mapping: Queue mapping for multiqueue devices
* @xmit_more: More SKBs are pending for this queue
* @ndisc_nodetype: router type (from link layer)
* @ooo_okay: allow the mapping of a socket to a queue to be changed
* @l4_hash: indicate hash is a canonical 4-tuple hash over transport
* ports.
* @sw_hash: indicates hash was computed in software stack
* @wifi_acked_valid: wifi_acked was set
* @wifi_acked: whether frame was acked on wifi or not
* @no_fcs: Request NIC to treat last 4 bytes as Ethernet FCS
* @napi_id: id of the NAPI struct this skb came from
* @secmark: security marking
* @offload_fwd_mark: fwding offload mark
* @mark: Generic packet mark
* @vlan_proto: vlan encapsulation protocol
* @vlan_tci: vlan tag control information
* @inner_protocol: Protocol (encapsulation)
* @inner_transport_header: Inner transport layer header (encapsulation)
* @inner_network_header: Network layer header (encapsulation)
* @inner_mac_header: Link layer header (encapsulation)
* @transport_header: Transport layer header
* @network_header: Network layer header
* @mac_header: Link layer header
* @tail: Tail pointer
* @end: End pointer
* @head: Head of buffer
* @data: Data head pointer
* @truesize: Buffer size
* @users: User count - see {datagram,tcp}.c
*/
struct sk_buff {
union {
struct {
/* These two members must be first. */
struct sk_buff *next;
struct sk_buff *prev;
union {
ktime_t tstamp;
struct skb_mstamp skb_mstamp;
};
};
struct rb_node rbnode; /* used in netem & tcp stack */
};
struct sock *sk;
struct net_device *dev;
/*
* This is the control buffer. It is free to use for every
* layer. Please put your private variables there. If you
* want to keep them across layers you have to do a skb_clone()
* first. This is owned by whoever has the skb queued ATM.
*/
char cb[48] __aligned(8);
unsigned long _skb_refdst;
void (*destructor)(struct sk_buff *skb);
#ifdef CONFIG_XFRM
struct sec_path *sp;
#endif
#if defined(CONFIG_NF_CONNTRACK) || defined(CONFIG_NF_CONNTRACK_MODULE)
struct nf_conntrack *nfct;
#endif
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
struct nf_bridge_info *nf_bridge;
#endif
unsigned int len,
data_len;
__u16 mac_len,
hdr_len;
/* Following fields are _not_ copied in __copy_skb_header()
* Note that queue_mapping is here mostly to fill a hole.
*/
kmemcheck_bitfield_begin(flags1);
__u16 queue_mapping;
__u8 cloned:1,
nohdr:1,
fclone:2,
peeked:1,
head_frag:1,
xmit_more:1;
/* one bit hole */
kmemcheck_bitfield_end(flags1);
/* fields enclosed in headers_start/headers_end are copied
* using a single memcpy() in __copy_skb_header()
*/
/* private: */
__u32 headers_start[0];
/* public: */
/* if you move pkt_type around you also must adapt those constants */
#ifdef __BIG_ENDIAN_BITFIELD
#define PKT_TYPE_MAX (7 << 5)
#else
#define PKT_TYPE_MAX 7
#endif
#define PKT_TYPE_OFFSET() offsetof(struct sk_buff, __pkt_type_offset)
__u8 __pkt_type_offset[0];
__u8 pkt_type:3;
__u8 pfmemalloc:1;
__u8 ignore_df:1;
__u8 nfctinfo:3;
__u8 nf_trace:1;
__u8 ip_summed:2;
__u8 ooo_okay:1;
__u8 l4_hash:1;
__u8 sw_hash:1;
__u8 wifi_acked_valid:1;
__u8 wifi_acked:1;
__u8 no_fcs:1;
/* Indicates the inner headers are valid in the skbuff. */
__u8 encapsulation:1;
__u8 encap_hdr_csum:1;
__u8 csum_valid:1;
__u8 csum_complete_sw:1;
__u8 csum_level:2;
__u8 csum_bad:1;
#ifdef CONFIG_IPV6_NDISC_NODETYPE
__u8 ndisc_nodetype:2;
#endif
__u8 ipvs_property:1;
__u8 inner_protocol_type:1;
__u8 remcsum_offload:1;
/* 3 or 5 bit hole */
#ifdef CONFIG_NET_SCHED
__u16 tc_index; /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
__u16 tc_verd; /* traffic control verdict */
#endif
#endif
union {
__wsum csum;
struct {
__u16 csum_start;
__u16 csum_offset;
};
};
__u32 priority;
int skb_iif;
__u32 hash;
__be16 vlan_proto;
__u16 vlan_tci;
#if defined(CONFIG_NET_RX_BUSY_POLL) || defined(CONFIG_XPS)
union {
unsigned int napi_id;
unsigned int sender_cpu;
};
#endif
union {
#ifdef CONFIG_NETWORK_SECMARK
__u32 secmark;
#endif
#ifdef CONFIG_NET_SWITCHDEV
__u32 offload_fwd_mark;
#endif
};
union {
__u32 mark;
__u32 reserved_tailroom;
};
union {
__be16 inner_protocol;
__u8 inner_ipproto;
};
__u16 inner_transport_header;
__u16 inner_network_header;
__u16 inner_mac_header;
__be16 protocol;
__u16 transport_header;
__u16 network_header;
__u16 mac_header;
/* private: */
__u32 headers_end[0];
/* public: */
/* These elements must be at the end, see alloc_skb() for details. */
sk_buff_data_t tail;
sk_buff_data_t end;
unsigned char *head,
*data;
unsigned int truesize;
atomic_t users;
};
在三层获取MAC地址的代码实现如下:
// net/netfilter/xt_mac.c
25 static bool mac_mt(const struct sk_buff *skb, struct xt_action_param *par)
26 {
27 const struct xt_mac_info *info = par->matchinfo;
28 bool ret;
29
30 if (skb->dev == NULL || skb->dev->type != ARPHRD_ETHER)
31 return false;
32 if (skb_mac_header(skb) < skb->head)
33 return false;
34 if (skb_mac_header(skb) + ETH_HLEN > skb->data)
35 return false;
36 ret = ether_addr_equal(eth_hdr(skb)->h_source, info->srcaddr);
37 ret ^= info->invert;
38 return ret;
39 }
车联网安全实践
车联网iptables相关实践包括对iptables规则的功能性应用、安全性应用和一些应用的技巧。
功能性应用
1)NAT
整车内各个需要联网的控制器均需要通过TBOX或5G模块上网,实现方式便是通过iptables的NAT功能:
iptables -t nat -A POSTROUTING -o em1 -s 192.168.205.160 -j SNAT --to 218.25.116.165
2)跨VLAN连通网络
通过VLAN划分广播域是保证车内网络安全的重要手段,但同时也会存在跨VLAN通信的需求,这时需要使用iptables来连接不同的VLAN:
iptables -A DFD_ACCESS_RULE -p TCP -s 172.11.20.0/24 -d 172.11.30.1 --dport XXX -j ACCEPT
安全性应用
1)默认拒绝策略
默认策略建议使用DROP的方式,然后将合法的连接以白名单的方式追加到规则链当中。
# default drop
iptables -P INPUT DROP
iptables -P OUTPUT ACCEPT
iptables -P FORWARD DROP
2)网络隔离
通过防火墙隔离车内和车外网络,VCU代表整车相关的电控功能单元,HU代表信息娱乐系统,通过iptables防火墙可以有效隔离整车网络和信息娱乐域:
3)高危端口过滤
高危端口包括SSH、远程诊断等敏感业务端口,通过iptalbes规则过滤并记录外部对高危端口的访问记录,可以有效阻止和发现潜在的攻击行为:
$IPTABLES -A INPUT -p tcp --dport 22 -j LOG --log-prefix='[IPT] DROP SSH I'
$IPTABLES -A INPUT -p tcp --dport 22 -j DROP
策略配置技巧
1)启用LOG模式
在研发早期开始启用LOG模式搜集车内未知的联网数据包,LOG模式通过-j LOG实现:
# LOG unknow packets
iptables -A INPUT -j LOG --log-ip-options --log-tcp-sequence --log-tcp-options --log-level 1 --log-prefix '[FIREWALL] DROP UNKNOW '
2)减少日志量
由于车内网络可能产生大量的数据连接,特别是自动驾驶相关的应用,其网络数据包的量可能极大,为了避免iptables产生过量的数据影响syslog正常记录内核日志,推荐使用iptables的connbytes模块,仅记录一个连接的前几个数据包,配置方式参考如下:
# Log only the first 3 packets
iptables -A INPUT -m connbytes --connbytes 1:3 --connbytes-mode packets --connbytes-dir both -j LOG --log-ip-options --log-tcp-sequence --log-tcp-options --log-level 1 --log-prefix '[FIREWALL] DROP UNKNOW '
iptables -A INPUT -j DROP
3)将通用规则放在底部
建议将更加严格的匹配规则放在规则列表的前面,将宽松的匹配规则放在后面,这样可以避免更加严格的规则无法生效的问题。
4)在策略规则的顶部设置ip白名单
如果需要设置ip白名单,建议在规则列表的顶部设置,原因同上。
参考文章
[1].?Things You Should Know About Netfilter - Sfvlughttps://thermicorp.de/netfilter/iptables/sfvlug.editthis.info/wiki/Things_You_Should_Know_About_Netfilter.html
|