USAGI IPv6 IPsec development for Linux
- 格式:pdf
- 大小:26.96 KB
- 文档页数:5
linux 搭建 ipv6 的 pppoe server 端近期在做PPP0EV6的测试,就尝试着在linux 搭建pppoe 相关软件,主要是开源软件 ppp-2.4.5 和 rp-pppoe-3.8以下是对网上资料的整理,跳过很多人写的乱七八糟的COPY 完全按下面步骤走就可以了 网络架构 pc ------------ router -------- serverPC: WIN7ROUTER:dlink 860lbServer:DEBIAN 5(这个安好就自带了 ppp rp-pppoe ,但下面我还是讲下如何用 GZ 来安,你可 以用 apt-get autoremove ppp pppoe 将它删了 )如何查看是否安好了,用 dpkg debian:/etc/ppp# dpkg -l | grep ppprc ppp 2.4.5-4 Point-to-Point Protocol (PPP ) - daemon rc pppoe 3.8-3 PPP over Ethernet driver debian:/etc/ppp# dpkg --get-selections | grep pppppppppoe如上,系统自带的被我删了,然后我自己装了这 2 个软件1. 下载 ppp-2.4.5.tar.gz 、rp-pppoe-3.8.tar.gz2. 安装a. 将 ppp 、rp-pppoe 解压b. 安装pppdcd ppp-2.4.5/pppdvi Makefile.linux 开启 HAVE_INET6=y // 关键点,不然不支持 IPV6cd ppp-2.4.5./configure(备注:先 make clean 一下,确保 HAVE_INET6开启)makemake installc. 安装 rp-pppoecd rp-pppoe/src./configure --enable-plugin=/opt/ppp-2.4.5// 这句好像没什么意义makemake install3. 修改配置文件vim /etc/ppp/pppoe.conf修改下面几个值,其他的不要动ETH=eth0USER=rp-pppoe //拨号用户名LINUX_PLUGIN=/etc/ppp/plugi ns/rp-pppoe.sovim /etc/ppp/pppoe-server-optio nsdeinstall deinstall下面是全部内容# PPP options for the PPPoE server# LIC: GPLrequire-chap // 表示会用至U chap-secrets 文件#logi nlcp-echo-i nterval 10lcp-echo-failure 2+ipv6ipv6 ::1,::2SERVER^分到一个FE80::1/10的地址,ROUTER会分到一个FE80::2/10的地址,然后我们需要配置dibbler才能让ROUTER拿到GLOBAL地址vim /etc/ppp/chap-secrets# Secrets for authe nticati on using CHAP# clie nt server secret IP addressesrp-pppoe * rp-pppoe *用*代替地址,这个表是CHAP认证的账号和密码对照表,所以实际上我们拨号的账号密码都是rp-pppoevim opti onslocal4. 开启server端pppoe-server -I eth05. 页面和配置效果PPPOEV6 ONLY设置»互联W» IPv6拨号成功以后可以看到PPP0 分配了一个本地地址FE80::2/10 ppp0 Link encap:Point-to-Point Protocol inet6 addr: fe80::2/10 Scope:LinkUP POINTOPOINT RUNNING NOARP MULTICASTMTU:1492 Metric:1RX packets:7 errors:0 dropped:0 overruns:0 frame:0TX packets:17 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:3RX bytes:196 (196.0 B) TX bytes:1291 (1.2 KiB)但是我们还没有拿到GLOBAL地址,所以我们需要在debian上跑radvd和dibbler来给PPP接口分配地址1. STATELES S情况,这种模式下只跑RADVD就可以了,RADVD的配置文件如下Vim /etc/radvd.confinterface ppp0 {AdvSendAdvert on;MinRtrAdvInterval 3;MaxRtrAdvInterval 10;AdvDefaultPreference high;AdvHomeAgentFlag off;IgnoreIfMissing on; // 这个很关键AdvManagedFlag off; // 这个很关键AdvOtherConfigFlag off; // 这个很关键prefix 2001::/96 {AdvOnLink on;AdvAutonomous on; // 让PPP接口根据PREFIX生成地址AdvRouterAddr on;};RDNSS 2001::1 2001::2 {AdvRDNSSPreference 8; AdvRDNSSLifetime 30;};};然后看效果,GLOBAL地址成功分配ppp0 Link encap:Point-to-Point Protocolinet6 addr: 2001::2/64 Scope:Globalinet6 addr: fe80::2/10 Scope:LinkUP POINTOPOINT RUNNING NOARP MULTICASTMTU:1492 Metric:1RX packets:47 errors:0 dropped:0 overruns:0 frame:0TX packets:13 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:3RX bytes:5452 (5.3 KiB) TX bytes:1439 (1.4 KiB)cat /etc/resolv.conf# Auto-Generatednameserver 192.168.0.1search但是RDNSS没有解析成功,没拿到DNS地址,看来只有换STATEFU蟆式2. STATEFU蟆式,既要配置RADVD又要配置DIBBLERVim /etc/radvd.conf 注意红色部分的不同interface ppp0{AdvSendAdvert on;MinRtrAdvInterval 3;MaxRtrAdvInterval 10;AdvDefaultPreference high;AdvHomeAgentFlag off;IgnoreIfMissing on;AdvManagedFlag on;AdvOtherConfigFlag on;prefix 2001::/96{AdvOnLink on;AdvAutonomous off;AdvRouterAddr on;};RDNSS 2001::1 2001::2AdvRDNSSPreference 8; AdvRDNSSLifetime 30;};};红色部分意思是RADVD不分配地址,用DIBBLER来分,原理就是协议上规定的M和0值Vim /etc/dibbler/server.conf# Logging level range: 1(Emergency)-8(Debug) log-level 8# Don't log full datelog-mode short# set preference of this server to 0 (higher = more prefered)preference 0{iface "ppp0" {// also ranges can be defines, instead of exact values t1 1800-2000t2 2700-3000prefered-lifetime 3600valid-lifetime 7200class {pool 2000::/64}# assign temporary addresses from this poolta-class {pool 3000::/96}#assign /96 prefixes from this poolpd-class {pd-pool 3000:458:ff01:ff03:abcd::/80pd-length 96}option dns-server 2000::ff,2000::fe}有时候会发生不回报文的问题,红色部分pppO表示在这个接口上跑dibbler然后我将pppO改成ethO,居然成功了,但第二次失败,又改回pppO,成功,后来我发现原来是因为PPPO接口建立之前带DIBBLER会不能通信,必须在PPP0建立后把DIBBLER带起来, 这个BUG看来需要自己研究配置文件才行了,暂时放着成功以后分得GLOBAL地址pppO Link en cap:Po in t-to-Po int Protocolinet6 addr: 2000::ea5c:2de2:713e:a561/96 Scope:Globalinet6 addr: fe80::2/10 Scope:Li nkUP POINTOPOINT RUNNING NOARP MULTICAS1MTU:1492 Metric:1RX packets:56 errors:。
IPv6和IPSec试验笔记1.IPv6 in Linux配置 (2)1.1.安装Debian (2)1.2.检查IPv6支持 (2)1.3.添加IPv6工具组件 (2)2.IPv6路由实验 (3)2.1.自动分配IPv6地址 (3)2.2.路由配置 (4)3.NAS:访问控制 (5)3.1.NAS网关原理试验:允许或禁止路由访问 (5)3.2.NAS部署试用:走TT (6)3.3.获得正式的IPv6地址:使用BTexact的IPv6隧道 (7)3.4.自动分配IPv4地址:DHCPv4 Server (7)3.5.登录界面:重定向页面 (8)3.4.1 Apache的配置 (8)4.NAS @ 211.86.49.110说明 (9)5.IPSec in Linux试验 (9)5.1.FreeS/WAN (10)5.2.KAME-racoon (10)5.2.1IPSec/IPv4 (10)5.2.2IPSec/IPv6 (11)5.2.3IPSec/IPv6 with CERT (12)5.3.isakmpd (13)6.Tunnel Broker (13)7.参考 (13)1.IPv6 in Linux配置Linux对IPv6的支持是比较彻底的,这得益于社区的共同努力,尤其是USAGI项目的持续努力,2.6内核保持了较好的IPv6新特性。
下面以Debian3.1r1a为例,介绍IPv6的安装和配置。
1.1.安装DebianDebian3.1r1a默认安装。
修正sources.list启用新的安装源:deb ftp://211.86.49.110/pub/ISOs/Debian3.1r1a/debian-31r1-i386-binary-1/ unstable contrib maindeb ftp://211.86.49.110/pub/ISOs/Debian3.1r1a/debian-31r1-i386-binary-2/ unstable contrib main然后更新dpkg库:#apt-get update为了方便后期使用,安装辅助工具:#apt-get install apt-show-versions // 查看已经安装组件列表#apt-get install rcconf //服务管理#apt-get install ssh samba less //常用工具为了操作方便,尤其是使用vmware虚拟机中安装的Linux时,建议从Windows中使用ssh 连接到linux,同时在Linux启用Samba服务,这样可以在Windows中方便的浏览Linux文件。
Linux高级网络安全指南实施IDS和IPS 在当前数字化时代,网络攻击已经成为一种常见的实体威胁。
为了保护计算机网络免受恶意攻击的侵害,实施有效的网络安全措施变得至关重要。
在这方面,IDS(入侵检测系统)和IPS(入侵防御系统)扮演着重要的角色。
本文将探讨如何在Linux系统上实施IDS和IPS。
一、入侵检测系统(IDS)IDS是一款能够监控网络流量、检测和报告潜在入侵情况的软件。
它可以通过分析网络流量的特征来检测异常行为,并及时生成警报,以提醒管理员采取必要的防御行动。
以下是在Linux系统上实施IDS 的步骤:1. 选择合适的IDS软件:市面上有许多优秀的开源IDS软件可供选择,如Snort、Suricata等。
根据实际需求选择适合的IDS软件,并确保其与Linux系统的兼容性。
2. 安装和配置IDS软件:使用适当的命令和软件包管理器安装所选的IDS软件。
安装完成后,根据文档和向导的指引进行配置,包括定义规则、设置日志记录等。
3. 启动和监控IDS系统:启动IDS软件并确保其正常运行。
监控IDS系统以获取实时警报,并及时响应异常事件。
二、入侵防御系统(IPS)IPS是一种能够主动防御网络入侵的技术。
与IDS不同,IPS不仅能够检测入侵行为,还能够采取自动化的措施来阻止入侵尝试。
以下是在Linux系统上实施IPS的步骤:1. 选择合适的IPS软件:市面上有多种高效的开源IPS软件可供选择,如Snort、Suricata等。
根据需求选择适合的IPS软件,并确保其与Linux系统的兼容性。
2. 安装和配置IPS软件:使用适当的命令和软件包管理器安装所选的IPS软件。
安装完成后,根据文档和向导的指引进行配置,设置阻止规则、启用自动阻止等。
3. 启动和监控IPS系统:启动IPS软件并确保其正常运行。
监控IPS系统以获取实时警报,并及时响应入侵尝试。
三、综合应用除了独立使用IDS和IPS,将它们结合起来可以提高网络安全性。
IPv6安全机制(IPSec)研究的开题报告一、选题背景和意义随着互联网的不断发展和IPv4地址的逐渐枯竭,IPv6作为下一代互联网协议已经广泛应用。
IPv6有着更加安全、高效、灵活等功能优势,但同时也带来了新的安全威胁和挑战。
在IPv6网络中,网络攻击者可以利用各种漏洞来入侵网络,窃取敏感信息、破坏网络稳定性。
因此,IPv6网络安全机制对于确保网络的安全性和可靠性具有重要的意义。
IPv6安全机制中的IPSec是一种使用加密算法、身份验证和完整性保护等技术来实现网络安全的协议集合。
IPSec支持在IPv6网络中进行加密传输和身份认证,可以保护IPv6数据包的机密性和完整性,保障网络数据的安全。
本研究旨在深入研究IPv6安全机制中的IPSec,探索其在IPv6网络中的应用和实现原理,分析其安全性和性能特点,为IPv6网络安全提供一种可靠的解决方案。
二、研究内容及方法1. IPv6网络安全现状分析。
对当前IPv6网络中存在的安全问题进行调查,分析其主要原因和影响,为后续的研究提供基础。
2. IPSec协议原理及实现技术研究。
对IPSec协议的加密算法、身份验证和完整性保护等技术进行深入研究,探索其在IPv6网络中的应用和实现原理。
3. IPSec安全性分析。
对IPSec协议的安全性进行分析和评估,探究其抵抗网络攻击和保障网络数据完整性的能力。
4. IPSec性能评估。
对IPSec协议在IPv6网络中的性能进行评估,分析其对网络延迟、吞吐量等性能指标的影响,从而为IPSec在实际应用场景下的性能调优提供依据。
研究方法主要包括文献调研、安全性评估、性能测试等。
三、预期成果及意义1. 对IPv6网络安全问题进行了全面的调研,揭示了当前IPv6网络中存在的安全威胁和风险。
2. 深入研究了IPv6安全机制中的IPSec协议,探究其在IPv6网络中的应用和实现原理。
3. 评估了IPSec协议的安全性和性能特点,为IPv6网络安全提供了一种可靠的解决方案。
Linux内核中的IPSEC实现(2)本文档的Copyleft归yfydz所有,使用GPL发布,可以自由拷贝,转载,转载时请保持文档的完整性,严禁用于任何商业用途。
msn: yfydz_no1@来源:4. 状态(xfrm_state)处理本节所介绍的函数都在net/xfrm/xfrm_state.c中定义。
4.1 状态分配状态分配函数为xfrm_state_alloc(), 该函数被pfkey_msg2xfrm_state()函数调用, pfkey_msg2xfrm_state()函数是将标准的pfkey_msg(SA结构)转换为xfrm状态, 同时该函数也被其他状态处理函数调用.struct xfrm_state *xfrm_state_alloc(void){struct xfrm_state *x;// 分配空间x = kzalloc(sizeof(struct xfrm_state), GFP_ATOMIC);if (x) {// 使用数初始化为1atomic_set(&x->refcnt, 1);// 被0个ipsec通道使用atomic_set(&x->tunnel_users, 0);// 初始化链表节点, 状态可按目的地址, 源地址和SPI挂接到不同链表INIT_HLIST_NODE(&x->bydst);INIT_HLIST_NODE(&x->bysrc);INIT_HLIST_NODE(&x->byspi);// 状态定时器init_timer(&x->timer);// 定时器处理函数x->timer.function = xfrm_timer_handler;x->timer.data = (unsigned long)x;// 回放检测定时器init_timer(&x->rtimer);// 回放定时器处理函数x->rtimer.function = xfrm_replay_timer_handler;x->rtimer.data = (unsigned long)x;x->curlft.add_time = (unsigned long)_sec; // SA生命期参数x->lft.soft_byte_limit = XFRM_INF;x->lft.soft_packet_limit = XFRM_INF;x->lft.hard_byte_limit = XFRM_INF;x->lft.hard_packet_limit = XFRM_INF;// 回放处理参数x->replay_maxage = 0;x->replay_maxdiff = 0;// 初始化状态锁spin_lock_init(&x->lock);}return x;}EXPORT_SYMBOL(xfrm_state_alloc);// 状态定时器超时处理函数static void xfrm_timer_handler(unsigned long data){struct xfrm_state *x = (struct xfrm_state*)data;unsigned long now = (unsigned long)_sec;long next = LONG_MAX;int warn = 0;spin_lock(&x->lock);// 如果该xfrm状态已经处于死亡状态, 可以返回了if (x->km.state == XFRM_STATE_DEAD)goto out;// 如果处于生命期到期状态, 转到期处理if (x->km.state == XFRM_STATE_EXPIRED)goto expired;// 如果到期了还要强制要增加一些时间if (x->lft.hard_add_expires_seconds) {// 计算强制增加的超时时间long tmo = x->lft.hard_add_expires_seconds +x->curlft.add_time - now;// 没法增加超时了, 到期if (tmo <= 0)goto expired;if (tmo < next)next = tmo;}// 如果到期了还要强制要增加的使用时间if (x->lft.hard_use_expires_seconds) {// 计算强制增加的使用时间long tmo = x->lft.hard_use_expires_seconds +(x->e_time ? : now) - now;// 没法增加超时了, 到期if (tmo <= 0)goto expired;if (tmo < next)next = tmo;}// dying表示软性增加超时已经不可用if (x->km.dying)goto resched;// 如果到期了还要软性要增加一些时间if (x->lft.soft_add_expires_seconds) {// 计算软性增加的时间long tmo = x->lft.soft_add_expires_seconds +x->curlft.add_time - now;// 软性增加超时不可用了if (tmo <= 0)warn = 1;else if (tmo < next)next = tmo;}// 如果到期了还要软性要增加的使用时间if (x->lft.soft_use_expires_seconds) {// 计算软性增加的使用时间long tmo = x->lft.soft_use_expires_seconds +(x->e_time ? : now) - now;// 软性增加超时不可用了if (tmo <= 0)warn = 1;else if (tmo < next)next = tmo;}// dying即为软性增加超时是否可用标志x->km.dying = warn;// 软性增加超时已比不可用, 进行状态的超时到期通知if (warn)km_state_expired(x, 0, 0);resched:// 如果增加的超时有效, 修改定时器超时时间if (next != LONG_MAX)mod_timer(&x->timer, jiffies + make_jiffies(next));goto out;expired:// 状态到期if (x->km.state == XFRM_STATE_ACQ && x->id.spi == 0) {// 如果这个状态是ACQ类型状态(不是用户空间主动建立的状态,而是内核根据策略主动要求// 用户空间进行IKE协商建立的状态)// 状态设置为到期x->km.state = XFRM_STATE_EXPIRED;// 唤醒等待队列准备进行垃圾搜集操作wake_up(&km_waitq);next = 2;goto resched;}// 删除状态, 进行状态的到期通知if (!__xfrm_state_delete(x) && x->id.spi)// 1表示是硬性到期了km_state_expired(x, 1, 0);out:spin_unlock(&x->lock);}// 回放定时器超时回调函数static void xfrm_replay_timer_handler(unsigned long data){struct xfrm_state *x = (struct xfrm_state*)data;spin_lock(&x->lock);// 只是状态为有效时才检查if (x->km.state == XFRM_STATE_VALID) {// 是否有NETLINK的监听者if (xfrm_aevent_is_on())// 通知回放超时事件xfrm_replay_notify(x, XFRM_REPLAY_TIMEOUT);else// 设置通知推迟标志x->xflags |= XFRM_TIME_DEFER;}spin_unlock(&x->lock);}状态初始化:int xfrm_init_state(struct xfrm_state *x){struct xfrm_state_afinfo *afinfo;int family = x->props.family;int err;err = -EAFNOSUPPORT;// 获取协议族信息结构afinfo = xfrm_state_get_afinfo(family);if (!afinfo)goto error;err = 0;// 协议族信息初始化if (afinfo->init_flags)err = afinfo->init_flags(x);xfrm_state_put_afinfo(afinfo);if (err)goto error;err = -EPROTONOSUPPORT;// 获取可用协议(ah, esp, ipcomp, ip)x->type = xfrm_get_type(x->id.proto, family);if (x->type == NULL)goto error;err = x->type->init_state(x);if (err)goto error;// 获取可用模式(transport, tunnel)x->mode = xfrm_get_mode(x->props.mode, family);if (x->mode == NULL)goto error;// 状态设置为VALIDx->km.state = XFRM_STATE_VALID;error:return err;}EXPORT_SYMBOL(xfrm_init_state);4.2 状态删除状态删除函数为xfrm_state_delete(), 该函数被pfkey_delete函数调用.// 这个函数只是__xfrm_state_delete()加锁的包裹函数int xfrm_state_delete(struct xfrm_state *x){int err;spin_lock_bh(&x->lock);err = __xfrm_state_delete(x);spin_unlock_bh(&x->lock);return err;}EXPORT_SYMBOL(xfrm_state_delete);// 实际的相同删除操作函数, 必须保证在x->lock加锁状态下执行int __xfrm_state_delete(struct xfrm_state *x){int err = -ESRCH;// 如果状态已经是DEAD就不操作了if (x->km.state != XFRM_STATE_DEAD) {// 设置状态为DEADx->km.state = XFRM_STATE_DEAD;// xfrm_state_lock是全局的状态链表操作锁spin_lock(&xfrm_state_lock);// 从目的地址索引的链表中断开hlist_del(&x->bydst);// 从源地址索引的链表中断开hlist_del(&x->bysrc);// 从SPI索引的链表中断开if (x->id.spi)hlist_del(&x->byspi);// xfrm状态总数减一xfrm_state_num--;spin_unlock(&xfrm_state_lock);/* All xfrm_state objects are created by xfrm_state_alloc.* The xfrm_state_alloc call gives a reference, and that* is what we are dropping here.*/// 减少该状态引用计数__xfrm_state_put(x);err = 0;}return err;}EXPORT_SYMBOL(__xfrm_state_delete);4.3 删除全部状态删除全部状态函数为xfrm_state_flush(), 该函数被pfkey_flush函数调用.// 删除某种协议proto的所有状态void xfrm_state_flush(u8 proto){int i;spin_lock_bh(&xfrm_state_lock);// 循环所有HASH链表for (i = 0; i <= xfrm_state_hmask; i++) {struct hlist_node *entry;struct xfrm_state *x;restart:// 在按目的地址进行索引的链表中循环hlist_for_each_entry(x, entry, xfrm_state_bydst+i, bydst) {// 要满足两个条件:// 非正在被ipsec通道使用的状态; 协议类型匹配if (!xfrm_state_kern(x) &&xfrm_id_proto_match(x->id.proto, proto)) {// 先hold住状态,防止在解开xfrm_state_lock锁, 又没被进入xfrm_state_delete()前// 被意外删除了, 此处考虑得比较仔细xfrm_state_hold(x);// 先解开xfrm_state_lock, 在xfrm_state_delete()中要重新上锁spin_unlock_bh(&xfrm_state_lock);// 删除状态xfrm_state_delete(x);// 减少刚才的引用计数xfrm_state_put(x);// 重新加锁, 循环spin_lock_bh(&xfrm_state_lock);goto restart;}}}spin_unlock_bh(&xfrm_state_lock);wake_up(&km_waitq);}EXPORT_SYMBOL(xfrm_state_flush);4.4 状态增加或更新状态增加函数为xfrm_state_add(), 状态更新函数为xfrm_state_update(),这两个函数都被pfkey_add函数调用.// 添加xfrm状态int xfrm_state_add(struct xfrm_state *x){struct xfrm_state *x1;int family;int err;// 当协议为为ESP, AH, COMP以及ANY时为真, 其他为假int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);family = x->props.family;spin_lock_bh(&xfrm_state_lock);// 根据xfrm的地址, SPI, 协议, 协议族等信息查找内核中是否已经存在相同的xfrm x1 = __xfrm_state_locate(x, use_spi, family);if (x1) {// 确实已经存在, 返回错误xfrm_state_put(x1);x1 = NULL;err = -EEXIST;goto out;}if (use_spi && x->km.seq) {// 如果序列号有效, 根据序列号查找内核中是否已经存在相同的xfrm x1 = __xfrm_find_acq_byseq(x->km.seq);// 找到, 但如果目的地址不符合的话, 仍试为没找到if (x1 && xfrm_addr_cmp(&x1->id.daddr, &x->id.daddr, family)) {xfrm_state_put(x1);x1 = NULL;}}// 如果没找到x1, 根据各种信息再查找xfrmif (use_spi && !x1)x1 = __find_acq_core(family, x->props.mode, x->props.reqid,x->id.proto,&x->id.daddr, &x->props.saddr, 0);// 如果x和现在内核中的xfrm匹配的话为x生成genid参数// 会用到一个静态单文件全局变量: xfrm_state_genid__xfrm_state_bump_genids(x);// 将新xfrm插入内核的各xfrm表, 这些表是以HASH表形式实现的, 分别根据// 源地址, 目的地址形成两个HASH表__xfrm_state_insert(x);err = 0;out:spin_unlock_bh(&xfrm_state_lock);// 如果按后来的条件找到x1, 删除之, 该状态不需要了if (x1) {// 将找到的x1从链表中删除,xfrm_state_delete(x1);// 释放x1xfrm_state_put(x1);}return err;}EXPORT_SYMBOL(xfrm_state_add);// 更新xfrm状态int xfrm_state_update(struct xfrm_state *x){struct xfrm_state *x1;int err;int use_spi = xfrm_id_proto_match(x->id.proto, IPSEC_PROTO_ANY);spin_lock_bh(&xfrm_state_lock);// 查找内核中相应的xfrm, 找不到的话出错x1 = __xfrm_state_locate(x, use_spi, x->props.family);err = -ESRCH;if (!x1)goto out;// 如果该xfrm正在被IPSEC通道使用, 返回错误if (xfrm_state_kern(x1)) {xfrm_state_put(x1);err = -EEXIST;goto out;}// 找到的x1本来就是在acquire状态, 直接将x插入系统xfrm表就行了if (x1->km.state == XFRM_STATE_ACQ) {__xfrm_state_insert(x);x = NULL;}err = 0;out:spin_unlock_bh(&xfrm_state_lock);if (err)return err;if (!x) {// 将找到的acquire状态的xfrm删除, 正确返回xfrm_state_delete(x1);xfrm_state_put(x1);return 0;}// 找到了x1, 状态也不是acquire, 即进行正常的更新x1中的数据为x的数据err = -EINVAL;spin_lock_bh(&x1->lock);if (likely(x1->km.state == XFRM_STATE_VALID)) {// 拷贝封装处理if (x->encap && x1->encap)memcpy(x1->encap, x->encap, sizeof(*x1->encap));// 拷贝care of的地址if (x->coaddr && x1->coaddr) {memcpy(x1->coaddr, x->coaddr, sizeof(*x1->coaddr));}// 没有SPI时拷贝选择子if (!use_spi && memcmp(&x1->sel, &x->sel, sizeof(x1->sel)))memcpy(&x1->sel, &x->sel, sizeof(x1->sel));// 拷贝生命期memcpy(&x1->lft, &x->lft, sizeof(x1->lft));x1->km.dying = 0;// 1秒钟的超时mod_timer(&x1->timer, jiffies + HZ);if (x1->e_time)xfrm_state_check_expire(x1);err = 0;}spin_unlock_bh(&x1->lock);xfrm_state_put(x1);return err;}EXPORT_SYMBOL(xfrm_state_update);4.5 状态插入状态插入函数为xfrm_state_insert(), 该函数被ipcomp_tunnel_attach()函数(net/ipv4/ipcomp.c)调用// xfrm_state_insert只是个包裹函数, 加xfrm_state_lock锁后调用__xfrm_state_bump_genids 和// __xfrm_state_insertvoid xfrm_state_insert(struct xfrm_state *x){spin_lock_bh(&xfrm_state_lock);__xfrm_state_bump_genids(x);__xfrm_state_insert(x);spin_unlock_bh(&xfrm_state_lock);}EXPORT_SYMBOL(xfrm_state_insert);/* xfrm_state_lock is held */// 碰撞检查, 看是否有多个连接状态, 要进行区别static void __xfrm_state_bump_genids(struct xfrm_state *xnew){unsigned short family = xnew->props.family;u32 reqid = xnew->props.reqid;struct xfrm_state *x;struct hlist_node *entry;unsigned int h;// 计算状态HASH值来找相关链表h = xfrm_dst_hash(&xnew->id.daddr, &xnew->props.saddr, reqid, family);hlist_for_each_entry(x, entry, xfrm_state_bydst+h, bydst) {// 如果已经在链表中的状态的协议族, 请求ID, 源地址, 目的地址都和新状态匹配if (x->props.family == family &&x->props.reqid == reqid &&!xfrm_addr_cmp(&x->id.daddr, &xnew->id.daddr, family) &&!xfrm_addr_cmp(&x->props.saddr, &xnew->props.saddr, family))// 将这些状态的genid参数设置为当前xfrm_state_genid(全局变量)x->genid = xfrm_state_genid;}}static void __xfrm_state_insert(struct xfrm_state *x){unsigned int h;// 将新状态的genid设置为当前xfrm_state_genid值加一,和其他碰撞的状态区分开x->genid = ++xfrm_state_genid;// 添加到按目的地址HASH的链表h = xfrm_dst_hash(&x->id.daddr, &x->props.saddr,x->props.reqid, x->props.family);hlist_add_head(&x->bydst, xfrm_state_bydst+h);// 添加到按源地址HASH的链表h = xfrm_src_hash(&x->id.daddr, &x->props.saddr, x->props.family);hlist_add_head(&x->bysrc, xfrm_state_bysrc+h);if (x->id.spi) {// 添加到按SPI进行HASH的链表h = xfrm_spi_hash(&x->id.daddr, x->id.spi, x->id.proto,x->props.family);hlist_add_head(&x->byspi, xfrm_state_byspi+h);}// 修改定时器, 超时仅1秒mod_timer(&x->timer, jiffies + HZ);// 如果设置了回放最大时间间隔, 超时改为该值if (x->replay_maxage)mod_timer(&x->rtimer, jiffies + x->replay_maxage);// 唤醒等待队列wake_up(&km_waitq);// 状态总数加1xfrm_state_num++;// HASH扩大检查, 检查是否需要扩展HASH表数量xfrm_hash_grow_check(x->bydst.next != NULL);}4.6 状态查找状态查找函数有好几个, 分别按不同条件来查找状态, 注意找到状态后, 都会增加状态的引用计数.4.6.1 xfrm_state_lookup// 只是__xfrm_state_lookup的包裹函数,是根据SPI进行HASH后查找struct xfrm_state *xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto,unsigned short family){struct xfrm_state *x;spin_lock_bh(&xfrm_state_lock);x = __xfrm_state_lookup(daddr, spi, proto, family);spin_unlock_bh(&xfrm_state_lock);return x;}EXPORT_SYMBOL(xfrm_state_lookup);static struct xfrm_state *__xfrm_state_lookup(xfrm_address_t *daddr, __be32 spi, u8 proto, unsigned short family){// 根据SPI进行HASHunsigned int h = xfrm_spi_hash(daddr, spi, proto, family);struct xfrm_state *x;struct hlist_node *entry;// 循环相应的SPI链表hlist_for_each_entry(x, entry, xfrm_state_byspi+h, byspi) {// 比较协议族, SPI, 和协议是否相同if (x->props.family != family ||x->id.spi != spi ||x->id.proto != proto)continue;// 比较目的地址是否相同switch (family) {case AF_INET:if (x->id.daddr.a4 != daddr->a4)continue;break;case AF_INET6:if (!ipv6_addr_equal((struct in6_addr *)daddr,(struct in6_addr *)x->id.daddr.a6))continue;break;};// 找到, 增加状态引用计数, 返回xfrm_state_hold(x);return x;}return NULL;}4.6.2 按地址查找状态// 只是__xfrm_state_lookup_byaddr的包裹函数,是根据目的地址进行HASH后查找struct xfrm_state *xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr,u8 proto, unsigned short family){struct xfrm_state *x;spin_lock_bh(&xfrm_state_lock);x = __xfrm_state_lookup_byaddr(daddr, saddr, proto, family);spin_unlock_bh(&xfrm_state_lock);return x;}EXPORT_SYMBOL(xfrm_state_lookup_byaddr);static struct xfrm_state *__xfrm_state_lookup_byaddr(xfrm_address_t *daddr, xfrm_address_t *saddr, u8 proto, unsigned short family){// 根据目的地址计算HASH值unsigned int h = xfrm_src_hash(daddr, saddr, family);struct xfrm_state *x;struct hlist_node *entry;// 循环相应的源地址链表hlist_for_each_entry(x, entry, xfrm_state_bysrc+h, bysrc) {// 比较协议族和协议是否相同if (x->props.family != family ||x->id.proto != proto)continue;// 比较源地址和目的地址是否相同switch (family) {case AF_INET:if (x->id.daddr.a4 != daddr->a4 ||x->props.saddr.a4 != saddr->a4)continue;break;case AF_INET6:if (!ipv6_addr_equal((struct in6_addr *)daddr,(struct in6_addr *)x->id.daddr.a6) ||!ipv6_addr_equal((struct in6_addr *)saddr,(struct in6_addr *)x->props.saddr.a6))continue;break;};// 找到, 增加状态引用计数, 返回xfrm_state_hold(x);return x;}return NULL;}4.6.3 __xfrm_state_locate这个函数只是__xfrm_state_lookup和__xfrm_state_lookup_byaddr的组合函数static inline struct xfrm_state *__xfrm_state_locate(struct xfrm_state *x, int use_spi, int family){if (use_spi)return __xfrm_state_lookup(&x->id.daddr, x->id.spi,x->id.proto, family);elsereturn __xfrm_state_lookup_byaddr(&x->id.daddr,&x->props.saddr,x->id.proto, family);}4.6.4 查找ACQUIRE类型的状态ACQUIRE类型的SA的产生是内核发现数据需要进行保护, 但却没有找到相关的SA, 就向用户空间的IKE协商程序发送ACQUIRE请求, 并生成一个ACQUIRE类型的SA, 如果用户空间协议协商成功会生成合适的SA传内核, 内核就会替换此ACQUIRE的SA, 因此ACQUIRE不是真正可用的SA, 只是表示有此SA的需求, 等待用户空间程序协商结果。
GRE隧道实验命令详解:ipv6 unicast-routing开启ipv6单播路由这条命令是配置ipv6是首先需要配置的,这点需要注意!no ip domain-lookup这是关闭路由器域名解析的命令,也很常用!ipv6 address 2001:aaaa::2/64这是设置端口ipv6地址的命令,64是代表ipv6地址的前缀!配置隧道Int tunnel 0tunnel source s1/0指定tunnel的源地址为s1/0tunnel destination 202.117.96.5指定tunnel的目标地址tunnel mode gre ipv6tunnel模式为ipv6的gre隧道ipv6的rip协议配置也和ipv4有所不同,这是需要注意的int fa0/0ipv6 rip test enable以上是在端口fa0/0端口上配置rip协议,rip协议别名为test 其它配置模式我将在回帖中说明!ISATAP隧道配置命令注意以下几条命令即可:no ipv6 nd suppress-ra启用隧道口的路由器广播tunnel mode ipv6ip isataptunnel模式为ipv6的ISATAP隧道NAT-PT配置ipv6 nat在接口上启用nat-ptipv6 nat prefix 2000:cccc::/96ipv6使用的网络前缀是2000:cccc::/96ipv6 nat v6v4 source 2000:cccc::2 192.168.1.1将ipv6地址2000:cccc::2静态映射为ipv4地址192.168.1.1R1配置清单:EnConfig tNo ip domain-loInt s0/0Ip add 202.100.2.1 255.255.255.0 No shutExitInt fa0/0Ipv6 add 2005:cccc::1/64No shutExitIpv6 unicast-routingInt tunnel 0Ipv6 add 2005:aaaa::1/64 Tunnel source 202.100.2.1 Tunnel destination 202.100.2.2 Tunnel mode gre ipExitIpv6 router rip testExitInt tunnel 0Ipv6 rip test enableInt fa1/0Ipv6 rip test enableR2配置清单EnConfig tNo ip domain-loInt s0/0Ip add 202.100.2.2 255.255.255.0 No shutExitInt fa0/0Ipv6 add 2004:cccc::1/64No shutExitIpv6 unicast-routingIpv6 router rip testExitInt tunnel 0Ipv6 add 2005:aaaa::2/64Tunnel source 202.100.2.2 Tunnel destination 202.100.2.1 Tunnel mode gre ipExitInt tunnel 0Ipv6 rip test enableInt fa1/0Ipv6 rip test enable好了,配置完成,让我们来看看实验结果!虚拟机真机实验成功!总结:这个实验书中错误极少,但是值得注意的地方有一处,在配置tunnel的时候必须将书中相应部分改成tunnel mode gre ip,否则将导致无法ping通,此处原理楼主的理解应该是,因为ipv6数据包是通过封装在ipv4数据包中传递的,所以必须将ipv6改成ip。
linux内核中的ipsec实现(6)--转载8. 安全协议与IPSEC相关的安全协议是AH(51)和ESP(50), IPSEC使用这两个协议对普通数据包进行封装, AH 只认证不加密,ESP既加密又认证, 当ESP和AH同时使用时, 一般都是先进行ESP封装, 再进行AH封装, 因为AH是对整个IP包进行验证的,而ESP只验证负载部分.在IPV4下的AH和ESP的协议实现在net/ipv4/ah4.c和net/ipv4/esp4.c中,每个协议实现实际是要完成两个结构: struct net_protocol和struct xfrm_type,前者用于处理接收的该协议类型的IP包, 后者则是IPSEC协议处理.8.1 AH8.1.1 初始化/* net/ipv4/ah4.c */static int __init ah4_init(void){// 登记AH协议的xfrm协议处理结构if (xfrm_register_type(&ah_type, AF_INET) < 0) { printk(KERN_INFO "ip ah init: can't add xfrm type\n"); return -EAGAIN;}// 登记AH协议到IP协议if (inet_add_protocol(&ah4_protocol, IPPROTO_AH) <0) {printk(KERN_INFO "ip ah init: can't add protocol\n");xfrm_unregister_type(&ah_type, AF_INET);return -EAGAIN;}return 0;}8.1.2 IPV4下的AH协议处理结构// AH协议处理结构, 接收到IPV4包后, 系统根据IP头中的protocol字段选择相应的上层协议处理// 函数, 当IP协议号是51时, 数据包将调用该结构的handler处理函数:static struct net_protocol ah4_protocol = {.handler = xfrm4_rcv,.err_handler = ah4_err,.no_policy = 1,};AH协议结构的handler函数为xfrm4_rcv, 在net/ipv4/xfrm4_input.c 中定义, 在上一篇中进行了介绍.// 错误处理, 收到ICMP错误包时的处理情况, 此时的skb包是ICMP包static void ah4_err(struct sk_buff *skb, u32 info){// 应用层, data指向ICMP错误包里的内部IP头struct iphdr *iph = (struct iphdr*)skb->data;// AH头struct ip_auth_hdr *ah = (structip_auth_hdr*)(skb->data+(iph->ihl<<2));struct xfrm_state *x;// ICMP错误类型检查, 本处理函数只处理"目的不可达"和"需要分片"两种错误if (skb->h.icmph->type != ICMP_DEST_UNREACH || skb->h.icmph->code != ICMP_FRAG_NEEDED) return;// 重新查找SAx = xfrm_state_lookup((xfrm_address_t*)&iph->daddr, ah->spi, IPPROTO_AH, AF_INET); if (!x)return;printk(KERN_DEBUG "pmtu discovery on SAAH/%08x/%08x\n",ntohl(ah->spi), ntohl(iph->daddr));xfrm_state_put(x);}8.1.3 AH4协议的IPSEC处理结构// AH4的xfrm协议处理结构static struct xfrm_type ah_type ={.description = "AH4",.owner = THIS_MODULE,.proto = IPPROTO_AH,// 状态初始化.init_state = ah_init_state,// 协议释放.destructor = ah_destroy,// 协议输入.input = ah_input,// 协议输出.output = ah_output};结构的重点是input和ouput函数8.1.3.1 状态初始化ah_data数据结构:/* include/net/ah.h */struct ah_data{// 密钥指针u8 *key;// 密钥长度int key_len;// 工作初始化向量u8 *work_icv;// 初始化向量完整长度int icv_full_len;// 初始化向量截断长度int icv_trunc_len;// HASH算法struct crypto_hash *tfm;};// 该函数被xfrm状态(SA)初始化函数xfrm_init_state调用// 用来生成SA中所用的AH数据处理结构相关信息static int ah_init_state(struct xfrm_state *x){struct ah_data *ahp = NULL;struct xfrm_algo_desc *aalg_desc;struct crypto_hash *tfm;// 对AH协议的SA, 认证算法是必须的, 否则就没法进行AH认证了if (!x->aalg)goto error;/* null auth can use a zero length key */// 认证算法密钥长度要大于512if (x->aalg->alg_key_len > 512)goto error;// 如果要进行UDP封装(进行NAT穿越), 错误, 因为AH是不支持NAT的if (x->encap)goto error;// 分配ah_data数据结构空间ahp = kzalloc(sizeof(*ahp), GFP_KERNEL);if (ahp == NULL)return -ENOMEM;// 设置AH数据结构的密钥和长度ahp->key = x->aalg->alg_key;ahp->key_len = (x->aalg->alg_key_len+7)/8;// 分配认证算法HASH结构指针并赋值给AH数据结构// 算法是固定相同的, 但在每个应用使用算法时的上下文是不同的, 该结构就是描述具体应用// 时的相关处理的上下文数据的tfm = crypto_alloc_hash(x->aalg->alg_name, 0, CRYPTO_ALG_ASYNC);if (IS_ERR(tfm))goto error;ahp->tfm = tfm;// 设置认证算法密钥if (crypto_hash_setkey(tfm, ahp->key, ahp->key_len)) goto error;/** Lookup the algorithm description maintained by xfrm_algo, * verify crypto transform properties, and store information* we need for AH processing. This lookup cannot fail here * after a successful crypto_alloc_hash().*/// 分配算法描述结构aalg_desc = xfrm_aalg_get_byname(x->aalg->alg_name, 0);BUG_ON(!aalg_desc);if (aalg_desc->uinfo.auth.icv_fullbits/8 !=crypto_hash_digestsize(tfm)) {printk(KERN_INFO "AH: %s digestsize %u != %hu\n",x->aalg->alg_name,crypto_hash_digestsize(tfm),aalg_desc->uinfo.auth.icv_fullbits/8);goto error;}// AH数据结构的初始化向量的总长和截断长度的赋值ahp->icv_full_len =aalg_desc->uinfo.auth.icv_fullbits/8;ahp->icv_trunc_len =aalg_desc->uinfo.auth.icv_truncbits/8;BUG_ON(ahp->icv_trunc_len >MAX_AH_AUTH_LEN);// 分配初始化向量空间, 没对其赋值, 其初始值就是随机值, 这也是初始化向量所需要的ahp->work_icv = kmalloc(ahp->icv_full_len,GFP_KERNEL);if (!ahp->work_icv)goto error;// AH类型SA中AH头长度: ip_auth_hdr结构和初始化向量长度, 按8字节对齐// 反映在AH封装操作时要将数据包增加的长度x->props.header_len = XFRM_ALIGN8(sizeof(structip_auth_hdr) + ahp->icv_trunc_len);// 如果是通道模式, 增加IP头长度if (x->props.mode == XFRM_MODE_TUNNEL)x->props.header_len += sizeof(struct iphdr);// SA数据指向AH数据结构x->data = ahp;return 0;error:if (ahp) {kfree(ahp->work_icv);crypto_free_hash(ahp->tfm);kfree(ahp);}return -EINV AL;}8.1.3.2 协议释放// 该函数被xfrm状态(SA)释放函数xfrm_state_gc_destroy()调用static void ah_destroy(struct xfrm_state *x){struct ah_data *ahp = x->data;if (!ahp)return;// 释放初始化向量空间kfree(ahp->work_icv);ahp->work_icv = NULL;// 算法描述释放crypto_free_hash(ahp->tfm);ahp->tfm = NULL;// AH数据结构释放kfree(ahp);}8.1.3.3 协议输入// 接收数据处理, 在xfrm4_rcv_encap()函数中调用// 进行AH认证, 剥离AH头static int ah_input(struct xfrm_state *x, struct sk_buff *skb) {int ah_hlen;int ihl;int err = -EINV AL;struct iphdr *iph;struct ip_auth_hdr *ah;struct ah_data *ahp;// IP头备份空间char work_buf[60];// skb数据包要准备留出AH头空间if (!pskb_may_pull(skb, sizeof(struct ip_auth_hdr)))goto out;// IP上层数据为AH数据ah = (struct ip_auth_hdr*)skb->data;// SA相关的AH处理数据ahp = x->data;ah_hlen = (ah->hdrlen + 2) << 2;// AH头部长度合法性检查if (ah_hlen != XFRM_ALIGN8(sizeof(struct ip_auth_hdr) + ahp->icv_full_len) &&ah_hlen != XFRM_ALIGN8(sizeof(struct ip_auth_hdr) + ahp->icv_trunc_len))goto out;// skb数据包要准备留出实际AH头空间if (!pskb_may_pull(skb, ah_hlen))goto out;/* We are going to _remove_ AH header to keep sockets happy, * so... Later this can change. */// 对于clone的包要复制成独立包if (skb_cloned(skb) &&pskb_expand_head(skb, 0, 0, GFP_ATOMIC))goto out;skb->ip_summed = CHECKSUM_NONE;// 可能包已经进行了复制, 所以对ah重新赋值ah = (struct ip_auth_hdr*)skb->data;iph = skb->nh.iph;// IP头长度ihl = skb->data - skb->nh.raw;// 备份外部IP头数据memcpy(work_buf, iph, ihl);// 将IP头中的一些参数清零, 这些参数不进行认证iph->ttl = 0;iph->tos = 0;iph->frag_off = 0;iph->check = 0;// IP头长度超过20字节时,处理IP选项参数if (ihl > sizeof(*iph)) {u32 dummy;if (ip_clear_mutable_options(iph, &dummy))goto out;}{// 认证数据缓冲区u8 auth_data[MAX_AH_AUTH_LEN];// 拷贝数据包中的认证数据到缓冲区memcpy(auth_data, ah->auth_data,ahp->icv_trunc_len);// 包括IP头部分数据skb_push(skb, ihl);// 计算认证值是否匹配, 非0表示出错err = ah_mac_digest(ahp, skb, ah->auth_data);// 认证失败返回错误if (err)goto out;err = -EINV AL;// 复制一定长度的认证数据作为初始化向量if (memcmp(ahp->work_icv, auth_data,ahp->icv_trunc_len)) {x->stats.integrity_failed++;goto out;}}// 将备份的IP头缓冲区中的协议改为AH内部包裹的协议((struct iphdr*)work_buf)->protocol = ah->nexthdr;// 将原来IP头数据拷贝到原来AH头后面作为新IP头skb->h.raw = memcpy(skb->nh.raw += ah_hlen,work_buf, ihl);// skb包缩减原来的IP头和AH头, 以新IP头作为数据开始__skb_pull(skb, ah_hlen + ihl);return 0;out:return err;}8.1.3.4 协议输出// 发送数据处理, 在xfrm4_output_one()中调用// 计算AH认证值, 添加AH头static int ah_output(struct xfrm_state *x, struct sk_buff *skb) {int err;struct iphdr *iph, *top_iph;struct ip_auth_hdr *ah;struct ah_data *ahp;// 临时IP头缓冲区, 最大IP头60字节union {struct iphdr iph;char buf[60];} tmp_iph;// 当前的IP头将作为最外部IP头top_iph = skb->nh.iph;// 临时IP头,用于临时保存IP头内部分字段数据iph = &tmp_iph.iph;// 将当前IP头中不进行认证的字段数据复制到临时IP头iph->tos = top_iph->tos;iph->ttl = top_iph->ttl;iph->frag_off = top_iph->frag_off;// 如果有IP选项, 处理IP选项if (top_iph->ihl != 5) {iph->daddr = top_iph->daddr;memcpy(iph+1, top_iph+1, top_iph->ihl*4 - sizeof(struct iphdr));err = ip_clear_mutable_options(top_iph,&top_iph->daddr);if (err)goto error;}// AH头定位在外部IP头后面, skb缓冲中已经预留出AH头的数据部分了,// 这是通过mode->output函数预留的, 通常调用type->output前要调用mode->oputputah = (struct ip_auth_hdr *)((char*)top_iph+top_iph->ihl*4);// AH中的下一个头用原来的外部IP头中的协议ah->nexthdr = top_iph->protocol;// 将外部IP头的不进行认证计算的部分字段清零top_iph->tos = 0;top_iph->tot_len = htons(skb->len);top_iph->frag_off = 0;top_iph->ttl = 0;// IP协议改为AHtop_iph->protocol = IPPROTO_AH;top_iph->check = 0;// AH数据处理结构ahp = x->data;// AH头长度对齐ah->hdrlen = (XFRM_ALIGN8(sizeof(struct ip_auth_hdr) +ahp->icv_trunc_len) >> 2) - 2;// AH头参数赋值ah->reserved = 0;// SPI值ah->spi = x->id.spi;// 序列号ah->seq_no = htonl(++x->replay.oseq);// 通知防止重放攻击处理, 更新序列号xfrm_aevent_doreplay(x);// 对skb进行AH认证值的计算err = ah_mac_digest(ahp, skb, ah->auth_data);if (err)goto error;// 赋值初始化向量值到认证数据部分memcpy(ah->auth_data, ahp->work_icv,ahp->icv_trunc_len);// 恢复原来IP头的的不认证部分的值top_iph->tos = iph->tos;top_iph->ttl = iph->ttl;top_iph->frag_off = iph->frag_off;if (top_iph->ihl != 5) {top_iph->daddr = iph->daddr;memcpy(top_iph+1, iph+1, top_iph->ihl*4 - sizeof(struct iphdr));}// 重新计算IP头的认证值ip_send_check(top_iph);err = 0;error:return err;}8.2 ESP8.2.1 初始化/* net/ipv4/esp4.c */static int __init esp4_init(void){// 登记ESP协议的xfrm协议处理结构if (xfrm_register_type(&esp_type, AF_INET) < 0) { printk(KERN_INFO "ip esp init: can't add xfrm type\n"); return -EAGAIN;}// 登记ESP协议到IP协议if (inet_add_protocol(&esp4_protocol, IPPROTO_ESP) < 0) {printk(KERN_INFO "ip esp init: can't add protocol\n");xfrm_unregister_type(&esp_type, AF_INET);return -EAGAIN;}return 0;}8.2.2 IPV4下的ESP协议处理结构// ESP协议处理结构, 接收到IPV4包后, 系统根据IP头中的protocol// 字段选择相应的上层协议处理函数, 当IP协议号是50时, 数据包将// 调用该结构的handler处理函数:static struct net_protocol esp4_protocol = {.handler = xfrm4_rcv,.err_handler = esp4_err,.no_policy = 1,};ESP协议结构的handler函数也是xfrm4_rcv, 在net/ipv4/xfrm4_input.c 中定义,在上一篇中进行了介绍.// 错误处理, 收到ICMP错误包时的处理情况, 此时的skb包是ICMP包static void esp4_err(struct sk_buff *skb, u32 info){// 应用层, data指向ICMP错误包里的内部IP头struct iphdr *iph = (struct iphdr*)skb->data;// ESP头struct ip_esp_hdr *esph = (structip_esp_hdr*)(skb->data+(iph->ihl<<2));struct xfrm_state *x;// ICMP错误类型检查, 本处理函数只处理"目的不可达"和"需要分片"两种错误if (skb->h.icmph->type != ICMP_DEST_UNREACH || skb->h.icmph->code != ICMP_FRAG_NEEDED) return;// 重新查找SAx = xfrm_state_lookup((xfrm_address_t*)&iph->daddr, esph->spi, IPPROTO_ESP,AF_INET);if (!x)return;NETDEBUG(KERN_DEBUG "pmtu discovery on SA ESP/%08x/%08x\n",ntohl(esph->spi), ntohl(iph->daddr));xfrm_state_put(x);}8.2.3 ESP4协议的IPSEC处理结构static struct xfrm_type esp_type ={.description = "ESP4",.owner = THIS_MODULE,.proto = IPPROTO_ESP,// 状态初始化.init_state = esp_init_state,// 协议释放.destructor = esp_destroy,// 计算最大长度.get_max_size = esp4_get_max_size,// 协议输入.input = esp_input,// 协议输出.output = esp_output};8.2.3.1 状态初始化esp_data数据结构:/* include/net/esp.h */struct esp_data{struct scatterlist sgbuf[ESP_NUM_FAST_SG];/* Confidentiality */// 加密使用的相关数据struct {// 密钥u8 *key; /* Key */// 密钥长度int key_len; /* Key length */// 填充长度int padlen; /* 0..255 *//* ivlen is offset from enc_data, where encrypted data start.* It is logically different of crypto_tfm_alg_ivsize(tfm).* We assume that it is either zero (no ivec), or* >= crypto_tfm_alg_ivsize(tfm). */// 初始化向量长度int ivlen;// 初始化向量是否初始化标志int ivinitted;// 初始化向量u8 *ivec; /* ivec buffer */// 加密算法struct crypto_blkcipher *tfm; /* crypto handle */ } conf;/* Integrity. It is active when icv_full_len != 0 */// 认证使用的相关数据struct {// 密钥u8 *key; /* Key */// 密钥长度int key_len; /* Length of the key */// 初始化向量u8 *work_icv;// 初始化向量全长int icv_full_len;// 初始化向量截断长度int icv_trunc_len;// 初始化向量更新函数, 好象没用void (*icv)(struct esp_data*,struct sk_buff *skb,int offset, int len, u8*icv);// HASH算法struct crypto_hash *tfm;} auth;};// ESP的esp_data数据结构初始化static int esp_init_state(struct xfrm_state *x){struct esp_data *esp = NULL;struct crypto_blkcipher *tfm;/* null auth and encryption can have zero length keys */// 如果有认证算法, 密钥至少512, ESP的认证处理是可选的, 但在实际中都会使用认证if (x->aalg) {if (x->aalg->alg_key_len > 512)goto error;}// ESP加密算法是必须的if (x->ealg == NULL)goto error;// 分配esp_data数据结构空间esp = kzalloc(sizeof(*esp), GFP_KERNEL);if (esp == NULL)return -ENOMEM;// 如果定义了认证算法, 初始化认证算法参数, 和AH类似if (x->aalg) {struct xfrm_algo_desc *aalg_desc;struct crypto_hash *hash;// 认证密钥和长度设置esp->auth.key = x->aalg->alg_key;esp->auth.key_len = (x->aalg->alg_key_len+7)/8; // 分配HASH算法的实现hash = crypto_alloc_hash(x->aalg->alg_name, 0, CRYPTO_ALG_ASYNC);if (IS_ERR(hash))goto error;esp->auth.tfm = hash;// 设置HASH算法密钥if (crypto_hash_setkey(hash, esp->auth.key,esp->auth.key_len))goto error;// 找到算法描述aalg_desc =xfrm_aalg_get_byname(x->aalg->alg_name, 0);BUG_ON(!aalg_desc);// 检查算法初始化向量长度合法性if (aalg_desc->uinfo.auth.icv_fullbits/8 !=crypto_hash_digestsize(hash)) {NETDEBUG(KERN_INFO "ESP: %sdigestsize %u != %hu\n",x->aalg->alg_name,crypto_hash_digestsize(hash),aalg_desc->uinfo.auth.icv_fullbits/8);goto error;}// 初始化向量的全长和截断长度esp->auth.icv_full_len =aalg_desc->uinfo.auth.icv_fullbits/8;esp->auth.icv_trunc_len =aalg_desc->uinfo.auth.icv_truncbits/8;// 分配全长度的初始化向量空间esp->auth.work_icv = kmalloc(esp->auth.icv_full_len, GFP_KERNEL);if (!esp->auth.work_icv)goto error;}// 初始化加密算法相关参数, ESP使用的加密算法都是对称块加密算法, 不可能用非对称算法的// 加密密钥esp->conf.key = x->ealg->alg_key;// 加密密钥长度esp->conf.key_len = (x->ealg->alg_key_len+7)/8; // 分配加密算法的具体实现结构tfm = crypto_alloc_blkcipher(x->ealg->alg_name, 0, CRYPTO_ALG_ASYNC);if (IS_ERR(tfm))goto error;esp->conf.tfm = tfm;// 初始化向量大小esp->conf.ivlen = crypto_blkcipher_ivsize(tfm);// 填充数据长度初始化为0esp->conf.padlen = 0;// 初始化向量长度非0, 分配具体的初始化向量空间if (esp->conf.ivlen) {esp->conf.ivec = kmalloc(esp->conf.ivlen,GFP_KERNEL);if (unlikely(esp->conf.ivec == NULL))goto error;esp->conf.ivinitted = 0;}// 设置加密算法密钥if (crypto_blkcipher_setkey(tfm, esp->conf.key,esp->conf.key_len))goto error;// 定义SA中ESP头部长度: ESP头加初始化向量长度// 反映在ESP封装操作时要将数据包增加的长度x->props.header_len = sizeof(struct ip_esp_hdr) + esp->conf.ivlen;// 如果是通道模式, 还需要增加IP头长度if (x->props.mode == XFRM_MODE_TUNNEL)x->props.header_len += sizeof(struct iphdr);// 如果要进行UDP封装if (x->encap) {struct xfrm_encap_tmpl *encap = x->encap; switch (encap->encap_type) {default:goto error;case UDP_ENCAP_ESPINUDP:// 该类型封装增加UDP头长度x->props.header_len += sizeof(struct udphdr);break;case UDP_ENCAP_ESPINUDP_NON_IKE:// 该类型封装增加UDP头长度外加加8字节x->props.header_len += sizeof(struct udphdr) + 2 * sizeof(u32);break;}}// 将esp_data作为SA的data指针x->data = esp;// 追踪长度, 最大增加长度和当前的计算的增加长度的差值,在路由时会用到// 对于AH, 由于没有定义get_max_size(), 该值位0x->props.trailer_len = esp4_get_max_size(x, 0) -x->props.header_len;return 0;error:x->data = esp;esp_destroy(x);x->data = NULL;return -EINV AL;}8.2.3.2 协议释放// 该函数被xfrm状态(SA)释放函数xfrm_state_gc_destroy()调用static void esp_destroy(struct xfrm_state *x){struct esp_data *esp = x->data;if (!esp)return;// 释放加密算法crypto_free_blkcipher(esp->conf.tfm);esp->conf.tfm = NULL;// 释放加密初始化向量kfree(esp->conf.ivec);esp->conf.ivec = NULL;// 释放认证算法crypto_free_hash(esp->auth.tfm);esp->auth.tfm = NULL;// 释放认证初始化向量kfree(esp->auth.work_icv);esp->auth.work_icv = NULL;// 释放esp_datakfree(esp);}8.2.3.3 计算最大长度// 在xfrm_state_mtu()函数中调用, 计算最大增加的数据长度// AH中没有该函数, 增加的长度使用x->props.header_len static u32 esp4_get_max_size(struct xfrm_state *x, int mtu) {struct esp_data *esp = x->data;// 加密块长度, 按4字节对齐u32 blksize =ALIGN(crypto_blkcipher_blocksize(esp->conf.tfm), 4);int enclen = 0;switch (x->props.mode) {case XFRM_MODE_TUNNEL:// 通道模式下的MTU, 按加密块大小对齐, +2是要包括2字节数据长度mtu = ALIGN(mtu +2, blksize);break;default:case XFRM_MODE_TRANSPORT:/* The worst case */// 传输模式下, MTU先按4字节对齐, 再加块长度减4mtu = ALIGN(mtu + 2, 4) + blksize - 4;break;case XFRM_MODE_BEET:/* The worst case. */enclen = IPV4_BEET_PHMAXLEN;mtu = ALIGN(mtu + enclen + 2, blksize);break;}// 如果加密算法中定义了填充长度, MTU也要按填充长度对齐if (esp->conf.padlen)mtu = ALIGN(mtu, esp->conf.padlen);// 返回MTU加提议中需要增加的头部长度和认证初始化向量的截断长度// enclen只在BEET模式下非0, 在通道和传输模式下都是0 return mtu + x->props.header_len +esp->auth.icv_trunc_len - enclen;}8.2.3.4 协议输入struct scatterlist结构说明:/* include/asm-i386/scatterlist.h */struct scatterlist {struct page *page;unsigned int offset;dma_addr_t dma_address;unsigned int length;};/** Note: detecting truncated vs. non-truncated authentication data is very* expensive, so we only support truncated data, which is the recommended* and common case.*/// 接收数据处理, 在xfrm4_rcv_encap()函数中调用// 进行ESP认证解密, 剥离ESP头, 解密成普通数据包, 数据包长度减少// 输入的数据包是ESP包static int esp_input(struct xfrm_state *x, struct sk_buff *skb) {struct iphdr *iph;struct ip_esp_hdr *esph;struct esp_data *esp = x->data;struct crypto_blkcipher *tfm = esp->conf.tfm;struct blkcipher_desc desc = { .tfm = tfm };struct sk_buff *trailer;int blksize = ALIGN(crypto_blkcipher_blocksize(tfm), 4);// 认证初始化向量截断长度int alen = esp->auth.icv_trunc_len;// 需要加密的数据长度: 总长减ESP头, 加密初始化向量长度, 认证初始化向量长度int elen = skb->len - sizeof(struct ip_esp_hdr) -esp->conf.ivlen - alen;int nfrags;int ihl;u8 nexthdr[2];struct scatterlist *sg;int padlen;int err;// 在skb头留出ESP头的空间if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr)))goto out;// 检查需要加密的数据长度, 必须大于0而且按块大小对齐的if (elen <= 0 || (elen & (blksize-1)))goto out;/* If integrity check is required, do this. */// 认证计算处理if (esp->auth.icv_full_len) {u8 sum[alen];// 计算认证值, 认证值保存在esp_data结构中err = esp_mac_digest(esp, skb, 0, skb->len - alen);if (err)goto out;// 将skb中的认证初始化向量部分数据拷贝到缓冲区sum中if (skb_copy_bits(skb, skb->len - alen, sum, alen))BUG();// 比较sum中的向量值和认证算法结构中的向量值是否匹配, 数据包正常情况下应该是相同的if (unlikely(memcmp(esp->auth.work_icv, sum, alen))) { x->stats.integrity_failed++;goto out;}}// 使数据包是可写的if ((nfrags = skb_cow_data(skb, 0, &trailer)) < 0) goto out;skb->ip_summed = CHECKSUM_NONE;// 定位在数据包中的ESP头位置, 为当前的data位置esph = (struct ip_esp_hdr*)skb->data;/* Get ivec. This can be wrong, check against another impls. */ // 设置加密算法的初始化向量if (esp->conf.ivlen)crypto_blkcipher_set_iv(tfm, esph->enc_data,esp->conf.ivlen);sg = &esp->sgbuf[0];if (unlikely(nfrags > ESP_NUM_FAST_SG)) {sg = kmalloc(sizeof(struct scatterlist)*nfrags,GFP_ATOMIC);if (!sg)goto out;}skb_to_sgvec(skb, sg, sizeof(struct ip_esp_hdr) +esp->conf.ivlen, elen);// 解密操作, 返回非0表示失败err = crypto_blkcipher_decrypt(&desc, sg, sg, elen);if (unlikely(sg != &esp->sgbuf[0]))kfree(sg);// 解密失败返回if (unlikely(err))return err;// 拷贝两字节数据if (skb_copy_bits(skb, skb->len-alen-2, nexthdr, 2))BUG();padlen = nexthdr[0];if (padlen+2 >= elen)goto out;/* ... check padding bits here. Silly. :-) */// 新的IP头iph = skb->nh.iph;ihl = iph->ihl * 4;// 如果是NAT穿越情况, 进行一些处理if (x->encap) {// xfrm封装模板struct xfrm_encap_tmpl *encap = x->encap;// 定位UDP数据头位置, 在IP头之后struct udphdr *uh = (void *)(skb->nh.raw + ihl);/** 1) if the NAT-T peer's IP or port changed then* advertize the change to the keying daemon.* This is an inbound SA, so just compare* SRC ports.*/// 如果IP头源地址和SA提议中的源地址不同或源端口不同if (iph->saddr != x->props.saddr.a4 ||uh->source != encap->encap_sport) { xfrm_address_t ipaddr;// 保存当前IP头源地址ipaddr.a4 = iph->saddr;// 进行NAT通知回调处理km_new_mapping(x, &ipaddr, uh->source);/* XXX: perhaps add an extra* policy check here, to see* if we should allow or* reject a packet from a* different source* address/port.*/}/** 2) ignore UDP/TCP checksums in case* of NAT-T in Transport Mode, or* perform other post-processing fixes* as per draft-ietf-ipsec-udp-encaps-06,* section 3.1.2*/// 如果是传输模式或BEET模式, 设置不需要计算校验和if (x->props.mode == XFRM_MODE_TRANSPORT || x->props.mode == XFRM_MODE_BEET) skb->ip_summed = CHECKSUM_UNNECESSARY;}// 新IP头中协议iph->protocol = nexthdr[1];// 缩减skb数据包长度pskb_trim(skb, skb->len - alen - padlen - 2);// 重新定位IP上层数据头位置skb->h.raw = __skb_pull(skb, sizeof(*esph) +esp->conf.ivlen) - ihl;return 0;out:return -EINV AL;}8.2.3.4 协议输出// 发送数据处理, 在xfrm4_output_one()中调用// 添加ESP头, 对数据包进行加密和认证处理, 数据包长度扩大// 在NAT穿越情况下会封装为UDP数据static int esp_output(struct xfrm_state *x, struct sk_buff *skb) {int err;struct iphdr *top_iph;struct ip_esp_hdr *esph;struct crypto_blkcipher *tfm;struct blkcipher_desc desc;struct esp_data *esp;struct sk_buff *trailer;int blksize;int clen;int alen;int nfrags;/* Strip IP+ESP header. */// 缩减skb数据, 减去IP头和ESP头, 剩下的数据就是要进行加密和认证的部分__skb_pull(skb, skb->h.raw - skb->data);/* Now skb is pure payload to encrypt */err = -ENOMEM;/* Round to block size */// 加密块的初始值clen = skb->len;// 获取SA的esp_data数据结构esp = x->data;// 认证初始化向量截断长度alen = esp->auth.icv_trunc_len;// 加密算法tfm = esp->conf.tfm;// 给块加密算法描述结构赋值desc.tfm = tfm;desc.flags = 0;// 每个加密块大小blksize = ALIGN(crypto_blkcipher_blocksize(tfm), 4); // 对齐要加密的数据总长clen = ALIGN(clen + 2, blksize);// 如果要考虑填充, 继续对齐if (esp->conf.padlen)clen = ALIGN(clen, esp->conf.padlen);// 使数据包可写if ((nfrags = skb_cow_data(skb, clen-skb->len+alen, &trailer)) < 0)goto error;/* Fill padding... */// 长度对齐后填充多余长度部分内容do {int i;for (i=0; i<clen-skb->len - 2; i++)*(u8*)(trailer->tail + i) = i+1;} while (0);// 最后两字节表示填充数据的长度*(u8*)(trailer->tail + clen-skb->len - 2) = (clen - skb->len)-2;pskb_put(skb, trailer, clen - skb->len);// 在将IP头部分扩展回来__skb_push(skb, skb->data - skb->nh.raw);// 现在的IP头作为外部IP头top_iph = skb->nh.iph;// esp头跟在IP头后esph = (struct ip_esp_hdr *)(skb->nh.raw +top_iph->ihl*4);// 数据总长增加认证部分长度top_iph->tot_len = htons(skb->len + alen);*(u8*)(trailer->tail - 1) = top_iph->protocol;/* this is non-NULL only with UDP Encapsulation */ if (x->encap) {// NAT穿越情况下要将数据封装为UDP包struct xfrm_encap_tmpl *encap = x->encap;struct udphdr *uh;u32 *udpdata32;// IP头后改为UDP头uh = (struct udphdr *)esph;// 填充UDP头参数, 源端口, 目的端口, UDP数据长度uh->source = encap->encap_sport;uh->dest = encap->encap_dport;uh->len = htons(skb->len + alen - top_iph->ihl*4); // 校验和为0, 表示不需要计算校验和, ESP本身就进行认证了uh->check = 0;switch (encap->encap_type) {default:case UDP_ENCAP_ESPINUDP:// 在该模式下ESP头跟在UDP头后面esph = (struct ip_esp_hdr *)(uh + 1);break;case UDP_ENCAP_ESPINUDP_NON_IKE:// 在该模式下ESP头跟在UDP头后面8字节处udpdata32 = (u32 *)(uh + 1);udpdata32[0] = udpdata32[1] = 0;esph = (struct ip_esp_hdr *)(udpdata32 + 2);break;}// 外部IP头协议是UDPtop_iph->protocol = IPPROTO_UDP;} else// 非NAT穿越情况下, 外部IP头中的协议是ESPtop_iph->protocol = IPPROTO_ESP;// 填充ESP头中的SPI和序列号esph->spi = x->id.spi;esph->seq_no = htonl(++x->replay.oseq);// 序列号更新通知回调xfrm_aevent_doreplay(x);// 如果加密初始化向量长度非零, 设置加密算法中的初始化向量if (esp->conf.ivlen) {if (unlikely(!esp->conf.ivinitted)) {get_random_bytes(esp->conf.ivec, esp->conf.ivlen);esp->conf.ivinitted = 1;}crypto_blkcipher_set_iv(tfm, esp->conf.ivec,esp->conf.ivlen);}// 加密操作do {struct scatterlist *sg = &esp->sgbuf[0];if (unlikely(nfrags > ESP_NUM_FAST_SG)) {sg = kmalloc(sizeof(struct scatterlist)*nfrags,GFP_ATOMIC);if (!sg)goto error;}skb_to_sgvec(skb, sg,esph->enc_data+esp->conf.ivlen-skb->data, clen); // 对数据加密err = crypto_blkcipher_encrypt(&desc, sg, sg, clen); if (unlikely(sg != &esp->sgbuf[0]))kfree(sg);} while (0);if (unlikely(err))goto error;// 将加密算法初始化向量拷贝到数据包if (esp->conf.ivlen) {memcpy(esph->enc_data, esp->conf.ivec,esp->conf.ivlen);crypto_blkcipher_get_iv(tfm, esp->conf.ivec,esp->conf.ivlen);}// 认证计算, 计算出HASH值并拷贝到数据包中if (esp->auth.icv_full_len) {err = esp_mac_digest(esp, skb, (u8 *)esph - skb->data, sizeof(*esph) + esp->conf.ivlen + clen); memcpy(pskb_put(skb, trailer, alen), esp->auth.work_icv, alen);}// 重新计算外部IP头校验和ip_send_check(top_iph);error:return err;}。
USAGI IPv6IPsec Development for LinuxMitsuru KandaToshiba CorporationCommunication Platform Laboratory Corporate Research&Development Center mk@isl.rdc.toshiba.co.jpKazunori MiyazawaYokogawa Electric Corporation Corporate Research and Development Headquarters Advanced Solitions Research CenterKazunori.Miyzawa@Hiroshi EsakiThe University of TokyoGraduate School of Information Science and Technologyhiroshi@wide.ad.jpAbstractUSAGI project[4]was founded to improve and develop Linux IPv6stack.We also developed(IPv6)IPsec stack for Linux kernel2.4and2.6series.we present our(IPv6)IPsec implementation(PE KEY,IPsec Security Association,Se-curity Policy,output processing,and input processing)for both2.4and2.6.1IntroductionIPsec is an archtectural system to provide security ser-vices in IP layer.On IPv4IPsec,it is difficult to introduce because we have to retorofit the existing TCP/IP(v4)stack. But on IPv6IPsec,it is embedded as a standard feature in the IPv6specification,which means it’s easy to deploy with IPv6.Linux kernel has been included the IPv6basic feature set since version2.2.Unfortunately IPv6IPsec was lacking in it.But the IPv4IPsec stack is provided by FreeS/WAN project[2]as separated from the mainline kernel(but a lot users who need IPv4IPsec are using it).At the beginning of the USAGI project,we discussed the design of the IPsec stack and decided to develop a Linux kernel2.4IPsec stack both IPv6and IPv4from scratch.Since Linux kernel2.5series,new networking architec-ture was introduced,called XFRM and/or Stackable Desti-nation.The latest Linux kernel version2.6supports IPsec both IP versions by using XFRM.In this paper,we’d like to talk about IPsec implementa-tion for Linux kernel2.4and2.6,then talk about our plan to develop a new key exchange daemon’Racoon2’in near the future.2IPsecIP Security(IPsec)architecture is described in RFC2401[8].IPsec consists of an IP packet process-ing part and a key exchanging part.IPsec processes a packet based on IPsec Security Policy(SP)and IPsec Secu-rity Association(SA).SP indicates which packet should be applied with IPsec,bypassed or dropped.When a packet is indicated’apply IPsec’by SP,IPsec stack process a packet with some parameters SA includes.SA is identified by a set of an IP destination address,an IPsec protocol and a Security Parameter Index(SPI).SA includes its identifier, algorithm,key,anti-replay counter,lifetime and so on.SA itself represents one IPsec protocol.If we want to apply a packet to multiple IPsec protocols(e.g.,AH+ESP),we use more than one SA(called SA bundle).SA is stored in IPsec Security Association Database(SAD).SP is stored in IPsec Security Policy Database(SPD).There are transfer mode and tunnel mode in IPsec archi-tecture.IPsec transfer mode is used for End-to-End com-munication.IPsec tunnel mode is mainly used for Security Gateway(SWG)to Security Gateway(e.g.,VPN1).PF KEY[9]interface is used to register/unregister SA parameters with SAD by the system administrator and the key exchange daemon.In addition,some implementations extend it to register/unregister SP with SPD because there is no specification for SPD interface.IPsec defines two packet formats,AH and ESP.AH is described in RFC2402[6],which provides connectionless integrity and data origin authentication for whole IP packet. ESP is described in RFC2406[7],which provides confiden-tiality,data origin authentication,connectionless integrity.1Virtual Private NetworkTo exchange IPsec key automaticaly,a lot of IPsec sys-tems use Internet Key Exchange Protocol(IKE[5]).3IPsec implementation for kernel2.4 In the Linux IPv4IPsec world,a lot of people use FreeS/W AN project’s implementation.It consists of an in-kernel IPsec processing part,Key Exchange daemon’Pluto’and some utility commands/scripts.To run Pluto with small changes on our IPsec ker-nel implementation and reduce impact for user who use FreeS/W AN implementation,we have decided to keep com-patibility with FreeS/WAN’s IPsec programming interface between kernel and userland.For this,we use the same PF KEY interface which FreeS/WAN project extended.In kernel IPsec packet processing part,we developed AH,ESP,SAD and SPD from scratch.3.1PF KEY interfacePF KEY(v2),which is described in RFC2367,is key management API mainly for IPsec.PF KEY is used for handling the IPsec Security Association Database.Ad-ditionally we have to handle the IPsec Security Policy Database,but there is no standard for the IPsec Security Policy management API.In FreeS/WAN implementation, PF KEY interface is extended to manage the IPsec Secu-rity Policy Database.Our kernel2.4IPsec implementation also uses the same PF KEY interface as FreeS/WAN’s one. It is important to be able to run the FreeS/WAN’s userland application(e.g.,Pluto)with small changes.3.2Encryption and Authentication algorithmWe provide HMAC-SHA1and HMAC-MD5for authen-tication,NULL,DES-CBC,3DES-CBS and AES for en-cryption.We thought encryption and authentication algo-rithm is not only used by IPsec and there are many algo-rithms so that we consider encryption and authentication al-gorithm and those interface should have good modularity. We adopted cipher modules which provided by CryptoAPI Project[1].3.3Security Association and Security PolicySA and SP themselves don’t depend substantially on the IP version.FreeS/WAN project architecture depends on their special virtual network interface for IPsec because it might focus on IPv4tunnel mode(Their implementation also provides IPv4transport mode).Their SA,SP,SAD and SPD also depend on their special virtual network interface. We considered and decided it was not suit to IPv6because the IPv6stack needed the neighbor discovery and the auto address configuration in its basic specification.If we had implemented IPv6IPsec stack with their architecture,we had to implement those basic specification in their special virtual network interface.Therefore we implemented our own SAD and SPD in order to handle both IPv4and IPv6.To improve the system performance,Each database will be locked by smallest granularity.And in many cases we use the’read lock’.SA and SP are managed by the reference counter to prevent used SA from removing by accident.3.4IPsec Packet Processing3.4.1OutputThere are various packet output paths from the IP(v4/6) layer to the network driver layer in Linux kernel net-working stack(TCP,UDP/ICMP,and NDP[10]for IPv6). The packets which may be applied IPsec will go through these paths.We had to add IPsec functional-ity for these output paths, e.g,in IPv6ip6xmit() for TCP,ip6build xmit()for UDP/ICMP and ndisc send ns()/ndisc send rs()for neighbor discovery packets.Output process is as follows(as shown in Figure1):1.check IPsec SP2.lookup the IPsec SA by the IPsec SP3.apply IPsec processing to the packet4.output the packet to the network driver layerTo reduce SA searhing time,we link the SP and the found SA after lookup from thefirst time.Find policyIPsecprocessingSPD SP SADB SAFind SAregarding toFigure1.IPsec Outputflow3.4.2InputAt input,there is only path for IP packets.We added IPsec processing part in ip6input finish().Input process is as follows(as shown in Figure2):1.receive the packet2.lookup the IPsec SA by SPI(which resides in AH/ESPheader)3.check integrity and decrypt4.check IPsec PolicyFind SA IPsec processing Check policySPD SP SADBSA Figure2.IPsec Inputflow3.5IPsec Tunnel modeWe are using IPv6-over-IPv6(and IPv4-over-IPv4)vir-tual tunnel device to implement IPsec tunnel mode.This implementation can avoid to duplication code of encapsula-tion/decapsulation outer IP header compairing with having these code in the IPsec processing part itself.The virtual tunnel device is not different from the normal IP-over-IP virtual tunnel device in Linux.4IPsec implementation for kernel2.6 In October2003,we submitted our IPsec code to Linux kernel network maintainers(David ler and Alexey Kuznetsov)for kernel2.52.At the same time,they were thinking about introducing the new network architecture which was included IPsec support.They liked our imple-mentation for it’s simplicity but they thought several de-tails should be handled very much differently.As the result,2kernel version2.5series is development version for next stable version 2.6unfortunately our implementation was not merged into the mainline kernel.The most important difference between ours and them is SAD/SPD part.They thought the whole SPD/SAD mecha-nism should beflow cache based lookup system shared by IPv4and IPv6.One month later,they introduced the new network architecture called’XFRM’to Linux kernel2.5. Atfirst their developing code lacked IPv6IPsec only for IPv4IPsec.In order to suport IPv6IPsec,we have imple-mented IPv6IPsec code based on XFRM(and discarded our original code).4.1PF KEY interfaceThe PF KEY interface of Linux kernel2.6(and2.5)is compatible with KAME[3]PF KEY interface.We can use ’setkey’command for configuring SA and SP and’Racoon’for IKE.Additionally we can add IPsec Policy each socket via Netlink3.They have suported only IPv4in theirfirst code,we have added IPv6support.4.2Security Association and Security PolicyOn the XFRM architecture,IPsec SP,which is rep-resented as xfrm policy structure,will be bound to the routingflow cache(and IPsec policy will point IPsec SA bundle)and IPsec SA,which is represented as xfrm state structure,is included in destination cache, dst entry structure.The chaining destination cache means IPsec SA bundle.4.3IPsec Packet Processing4.3.1OutputThe output part of the XFRM architecture is placed between the IP layer and the network driver layer.In general,non IPsec packet will be passed to the network driver layer by a single destination output function,which is resolved routing lookup.But IPsec packet will be need to apply some IPsec processing(e.g.,encryption,hash).XFRM functions make a chain of destination output functions(We call Stackable Destination,as shown in Figure3).Each function match each IPsec processing(AH,ESP and IPcomp[11]).To be more specific,in order to pass a packet to the net-work driver layer we have to do as follows(as shown in Figure4):1.lookup routing table to decide output function byip6route output()2.lookup IPsec Security Policy3Netlink is used to transfer network information between kernel and userland applications.3.lookup IPsec Security Association(s)suitable for IPsecSecurity Policy and create destination chain4.to apply IPsec,pass a packet to the destination chainFigure3.Stackable DestinationFigure4.XFRM outputflow4.3.2InputThe input part of the XFRM architecture is simpler than out-put.The XFRM input function is handled as same as upper layer protocols like TCP,UDP,etc.In IPv6,IPsec headers are defined as IPv6extension header but IPsec input func-tions are handled as an upper layer protocol handler.As the result of introducing IPv6IPsec input processing in Linux kernel,inconsistencies existed between IPsec headers and other IPv6extension headers.In order to resolve this,we moved to the other IPv6extension header handler functions to upper layer protocol handler.In detail,we registered IPsec header(both AH and ESP)handler functions with up-per layer protocol handler array inet6protos[].Incoming IPsec packet processingflow is as follows(as shown in Figure5):1.process IP packet from IP header in sequence2.process IPsec part(check integrity and decrypt)iffounded3.check IPsec Security Policy4.pass IP packet next handlerFigure5.XFRM inputflow4.4IPsec Tunnel modeLinux kernel2.6IPsec tunnel mode doesn’t use the vir-tual tunnel device to create tunnel.The IPsec stack builds the outer IP header during IPsec processing by itself.5Future WorkThe XFRM architecture is not only for IPsec but also for other extension headers processing such as Mobile IPv6. We will improve the XFRM architecture to make it be more flexible.At IETF IP Security working group(IPsec wg),New In-ternet Key exchange protocol(IKEv2)is now discussed.In order to catch up with the New version of IKE,we have a plan to develop the new key exchange application,Racoon2. IKE is not only Key Exchange protocol for IPsec.Racoon2 will support for multiple Key exchange protocols IKEv1, IKEv2and Kink.And also at IETF IPsec wg,new versions of ESP and AH is discussed.we will catch up with them as well.6ConclusionFrom the new Linux kernel stable version2.6,we will able to use IPsec by default,there will be no need to add extra patches,recompile kernel.References[1]CryptoAPI Project./.[2]FreeS/WAN Project./.[3]KAME Project./.[4]USAGI Project./.[5] D.C.D.Harkins.The Internet Key Exchange.RFC2409,November1998.[6]S.Kent and R.Atkinson.IP Authentication Header.RFC2402,November1998.[7]S.Kent and R.Atkinson.IP Encapsulating Security Payload.RFC2406,November1998.[8]S.Kent and R.Atkinson.Security Architecture for the In-ternet Protocol.RFC2401,November1998.[9] D.McDonald,C.Metz,and B.Phan.PF KEY Key Man-agement API,Version2.RFC2367,July1998.[10]T.Narten,E.Nordmark,and W.Simpson.Neighbor Dis-covery for IP Version6(IPv6).RFC2461,December1998.[11] A.Shacham,B.Monsour,R.Pereira,and M.Thomas.IPPayload Compression Protocol.RFC3173,September2001.。