Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
I
iproute2
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
iproute2
Commits
9111bd8e
Commit
9111bd8e
authored
Jul 06, 2004
by
net[shemminger]!shemminger
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Import patch ipxfrm-20040705.diff
(Logical change 1.51)
parent
7440b0b5
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
3212 additions
and
0 deletions
+3212
-0
iproute2/ip/ipxfrm.c
iproute2/ip/ipxfrm.c
+1054
-0
iproute2/ip/xfrm.h
iproute2/ip/xfrm.h
+104
-0
iproute2/ip/xfrm_policy.c
iproute2/ip/xfrm_policy.c
+1292
-0
iproute2/ip/xfrm_state.c
iproute2/ip/xfrm_state.c
+762
-0
No files found.
iproute2/ip/ipxfrm.c
View file @
9111bd8e
/* $USAGI: $ */
/*
* Copyright (C)2004 USAGI/WIDE Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* based on ip.c, iproute.c
*/
/*
* Authors:
* Masahide NAKAMURA @USAGI
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <time.h>
#include <netdb.h>
#include <net/if.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/xfrm.h>
#include "utils.h"
#include "xfrm.h"
struct
xfrm_filter
filter
;
static
void
usage
(
void
)
__attribute__
((
noreturn
));
static
void
usage
(
void
)
{
fprintf
(
stderr
,
"Usage: ip xfrm XFRM_OBJECT { COMMAND | help }
\n
"
"where XFRM_OBJECT := { state | policy }
\n
"
);
exit
(
-
1
);
}
#ifdef USE_MIP6
struct
typeent
{
char
*
t_name
;
int
t_type
;
};
static
const
struct
typeent
mh_types
[]
=
{
{
"brr"
,
0
},
{
"hoti"
,
1
},
{
"coti"
,
2
},
{
"hot"
,
3
},
{
"cot"
,
4
},
{
"bu"
,
5
},
{
"ba"
,
6
},
{
"be"
,
7
},
{
NULL
,
-
1
}
};
static
const
struct
typeent
*
getmhtypebyname
(
const
char
*
name
)
{
int
i
;
int
max
=
sizeof
(
mh_types
)
/
sizeof
(
mh_types
[
0
]);
for
(
i
=
0
;
i
<
max
;
i
++
)
{
if
(
mh_types
[
i
].
t_name
==
NULL
)
break
;
if
(
strcmp
(
name
,
mh_types
[
i
].
t_name
)
==
0
)
return
&
mh_types
[
i
];
}
return
NULL
;
}
static
const
struct
typeent
*
getmhtypebynumber
(
__u8
proto
)
{
int
i
;
int
max
=
sizeof
(
mh_types
)
/
sizeof
(
mh_types
[
0
]);
for
(
i
=
0
;
i
<
max
;
i
++
)
{
if
(
mh_types
[
i
].
t_name
==
NULL
)
break
;
if
(
proto
==
mh_types
[
i
].
t_type
)
return
&
mh_types
[
i
];
}
return
NULL
;
}
#endif
const
char
*
strxf_flags
(
__u8
flags
)
{
static
char
str
[
16
];
const
int
sn
=
sizeof
(
flags
)
*
8
-
1
;
__u8
b
;
int
i
=
0
;
for
(
b
=
(
1
<<
sn
);
b
>
0
;
b
>>=
1
)
str
[
i
++
]
=
((
b
&
flags
)
?
'1'
:
'0'
);
str
[
i
]
=
'\0'
;
return
str
;
}
const
char
*
strxf_share
(
__u8
share
)
{
static
char
str
[
32
];
switch
(
share
)
{
case
XFRM_SHARE_ANY
:
strcpy
(
str
,
"any"
);
break
;
case
XFRM_SHARE_SESSION
:
strcpy
(
str
,
"session"
);
break
;
case
XFRM_SHARE_USER
:
strcpy
(
str
,
"user"
);
break
;
case
XFRM_SHARE_UNIQUE
:
strcpy
(
str
,
"unique"
);
break
;
default:
sprintf
(
str
,
"unknown-share(%d)"
,
share
);
break
;
}
return
str
;
}
void
xfrm_id_info_print
(
xfrm_address_t
*
saddr
,
struct
xfrm_id
*
id
,
__u8
mode
,
__u32
reqid
,
__u16
family
,
FILE
*
fp
,
const
char
*
prefix
)
{
char
abuf
[
256
];
__u32
spi
;
struct
protoent
*
pp
;
char
pbuf
[
32
];
char
*
p
;
if
(
prefix
)
fprintf
(
fp
,
prefix
);
memset
(
abuf
,
'\0'
,
sizeof
(
abuf
));
fprintf
(
fp
,
"%s "
,
rt_addr_n2a
(
family
,
sizeof
(
*
saddr
),
saddr
,
abuf
,
sizeof
(
abuf
)));
memset
(
abuf
,
'\0'
,
sizeof
(
abuf
));
fprintf
(
fp
,
"%s
\n
"
,
rt_addr_n2a
(
family
,
sizeof
(
id
->
daddr
),
&
id
->
daddr
,
abuf
,
sizeof
(
abuf
)));
if
(
prefix
)
fprintf
(
fp
,
prefix
);
fprintf
(
fp
,
"
\t
"
);
pp
=
getprotobynumber
(
id
->
proto
);
if
(
pp
)
p
=
pp
->
p_name
;
else
{
sprintf
(
pbuf
,
"%d"
,
id
->
proto
);
p
=
pbuf
;
}
switch
(
id
->
proto
)
{
case
IPPROTO_ESP
:
case
IPPROTO_AH
:
case
IPPROTO_COMP
:
case
IPPROTO_IPV6
:
/* XXX: for testing */
fprintf
(
fp
,
"%s "
,
p
);
break
;
#ifdef USE_MIP6
case
IPPROTO_ROUTING
:
fprintf
(
fp
,
"route2[%s] "
,
p
);
break
;
case
IPPROTO_DSTOPTS
:
fprintf
(
fp
,
"hao[%s] "
,
p
);
break
;
#endif
default:
fprintf
(
fp
,
"unspec(%s)"
,
p
);
break
;
}
switch
(
id
->
proto
)
{
case
IPPROTO_ESP
:
case
IPPROTO_AH
:
case
IPPROTO_COMP
:
case
IPPROTO_IPV6
:
/* XXX: for testing */
default:
spi
=
ntohl
(
id
->
spi
);
fprintf
(
fp
,
"spi %d(0x%08x) "
,
spi
,
spi
);
break
;
#ifdef USE_MIP6
case
IPPROTO_ROUTING
:
case
IPPROTO_DSTOPTS
:
/*
* For RT2/HAO, spi is specifyed 0 value from the userland.
* After the kernel creates a state for it, it is updated an
* identical value for a source address by the kernel.
* It is stored by host-byte order.
*/
spi
=
id
->
spi
;
fprintf
(
fp
,
"(saddr-spi %d) "
,
spi
);
break
;
#endif
}
fprintf
(
fp
,
"reqid %d "
,
reqid
);
fprintf
(
fp
,
"%s
\n
"
,
(
mode
?
"tunnel"
:
"transport"
));
}
static
const
char
*
strxf_limit
(
__u64
limit
)
{
static
char
str
[
32
];
if
(
limit
==
XFRM_INF
)
strcpy
(
str
,
"(INF)"
);
else
sprintf
(
str
,
"%llu"
,
limit
);
return
str
;
}
void
xfrm_stats_print
(
struct
xfrm_stats
*
s
,
FILE
*
fp
,
const
char
*
prefix
)
{
if
(
prefix
)
fprintf
(
fp
,
prefix
);
fprintf
(
fp
,
"stats:
\n
"
);
if
(
prefix
)
fprintf
(
fp
,
prefix
);
fprintf
(
fp
,
" "
);
fprintf
(
fp
,
"replay-window %d "
,
s
->
replay_window
);
fprintf
(
fp
,
"replay %d "
,
s
->
replay
);
fprintf
(
fp
,
"failed %d"
,
s
->
integrity_failed
);
fprintf
(
fp
,
"
\n
"
);
}
static
const
char
*
strxf_time
(
__u64
time
)
{
static
char
str
[
32
];
struct
tm
*
tp
;
time_t
t
;
if
(
time
==
0
)
{
strcpy
(
str
,
"(undefined)"
);
}
else
{
/* XXX: treat time in the same manner of xfrm_{user,state}.c */
t
=
(
long
)
time
;
tp
=
localtime
(
&
t
);
sprintf
(
str
,
"%04d/%02d/%02d %02d:%02d:%02d"
,
tp
->
tm_year
+
1900
,
tp
->
tm_mon
+
1
,
tp
->
tm_mday
,
tp
->
tm_hour
,
tp
->
tm_min
,
tp
->
tm_sec
);
}
return
str
;
}
void
xfrm_lifetime_print
(
struct
xfrm_lifetime_cfg
*
cfg
,
struct
xfrm_lifetime_cur
*
cur
,
FILE
*
fp
,
const
char
*
prefix
)
{
if
(
cfg
)
{
if
(
prefix
)
fprintf
(
fp
,
prefix
);
fprintf
(
fp
,
"lifetime config:
\n
"
);
if
(
prefix
)
fprintf
(
fp
,
prefix
);
fprintf
(
fp
,
" "
);
fprintf
(
fp
,
"limit: "
);
fprintf
(
fp
,
"soft "
);
fprintf
(
fp
,
strxf_limit
(
cfg
->
soft_byte_limit
));
fprintf
(
fp
,
"(bytes), hard "
);
fprintf
(
fp
,
strxf_limit
(
cfg
->
hard_byte_limit
));
fprintf
(
fp
,
"(bytes)
\n
"
);
if
(
prefix
)
fprintf
(
fp
,
prefix
);
fprintf
(
fp
,
" "
);
fprintf
(
fp
,
"limit: "
);
fprintf
(
fp
,
"soft "
);
fprintf
(
fp
,
strxf_limit
(
cfg
->
soft_packet_limit
));
fprintf
(
fp
,
"(packets), hard "
);
fprintf
(
fp
,
strxf_limit
(
cfg
->
hard_packet_limit
));
fprintf
(
fp
,
"(packets)
\n
"
);
if
(
prefix
)
fprintf
(
fp
,
prefix
);
fprintf
(
fp
,
" "
);
fprintf
(
fp
,
"expire add: "
);
fprintf
(
fp
,
"soft "
);
fprintf
(
fp
,
"%llu"
,
cfg
->
soft_add_expires_seconds
);
fprintf
(
fp
,
"(sec), hard "
);
fprintf
(
fp
,
"%llu"
,
cfg
->
hard_add_expires_seconds
);
fprintf
(
fp
,
"(sec)
\n
"
);
if
(
prefix
)
fprintf
(
fp
,
prefix
);
fprintf
(
fp
,
" "
);
fprintf
(
fp
,
"expire use: "
);
fprintf
(
fp
,
"soft "
);
fprintf
(
fp
,
"%llu"
,
cfg
->
soft_use_expires_seconds
);
fprintf
(
fp
,
"(sec), hard "
);
fprintf
(
fp
,
"%llu"
,
cfg
->
hard_use_expires_seconds
);
fprintf
(
fp
,
"(sec)
\n
"
);
}
if
(
cur
)
{
if
(
prefix
)
fprintf
(
fp
,
prefix
);
fprintf
(
fp
,
"lifetime current:
\n
"
);
if
(
prefix
)
fprintf
(
fp
,
prefix
);
fprintf
(
fp
,
" "
);
fprintf
(
fp
,
"%llu(bytes), "
,
cur
->
bytes
);
fprintf
(
fp
,
"%llu(packets)
\n
"
,
cur
->
packets
);
if
(
prefix
)
fprintf
(
fp
,
prefix
);
fprintf
(
fp
,
" "
);
fprintf
(
fp
,
"add %s "
,
strxf_time
(
cur
->
add_time
));
fprintf
(
fp
,
"use %s"
,
strxf_time
(
cur
->
use_time
));
fprintf
(
fp
,
"
\n
"
);
}
}
void
xfrm_selector_print
(
struct
xfrm_selector
*
sel
,
__u16
family
,
FILE
*
fp
,
const
char
*
prefix
)
{
char
abuf
[
256
];
__u16
f
;
f
=
sel
->
family
;
if
(
f
==
AF_UNSPEC
)
f
=
family
;
if
(
f
==
AF_UNSPEC
)
f
=
preferred_family
;
if
(
prefix
)
fprintf
(
fp
,
prefix
);
switch
(
sel
->
proto
)
{
case
IPPROTO_ICMPV6
:
{
__u16
type
=
ntohs
(
sel
->
xfrmsel_icmp_type
);
__u16
code
=
ntohs
(
sel
->
xfrmsel_icmp_code
);
memset
(
abuf
,
'\0'
,
sizeof
(
abuf
));
fprintf
(
fp
,
"%s/%d"
,
rt_addr_n2a
(
f
,
sizeof
(
sel
->
saddr
),
&
sel
->
saddr
,
abuf
,
sizeof
(
abuf
)),
sel
->
prefixlen_s
);
memset
(
abuf
,
'\0'
,
sizeof
(
abuf
));
fprintf
(
fp
,
" %s/%d"
,
rt_addr_n2a
(
f
,
sizeof
(
sel
->
daddr
),
&
sel
->
daddr
,
abuf
,
sizeof
(
abuf
)),
sel
->
prefixlen_d
);
fprintf
(
fp
,
"
\n
"
);
if
(
prefix
)
fprintf
(
fp
,
prefix
);
fprintf
(
fp
,
"
\t
"
);
fprintf
(
fp
,
"upspec %u %u,%u "
,
sel
->
proto
,
type
,
code
);
break
;
}
#ifdef USE_MIP6
case
IPPROTO_MH
:
{
__u16
type
=
ntohs
(
sel
->
xfrmsel_mh_type
);
char
mhname
[
32
];
if
(
sel
->
xfrmsel_mh_type_mask
==
0
)
strcpy
(
mhname
,
"any"
);
else
{
const
struct
typeent
*
tp
;
tp
=
getmhtypebynumber
(
type
);
if
(
tp
)
strcpy
(
mhname
,
tp
->
t_name
);
else
strcpy
(
mhname
,
"unknown-mh"
);
}
memset
(
abuf
,
'\0'
,
sizeof
(
abuf
));
fprintf
(
fp
,
"%s/%d"
,
rt_addr_n2a
(
f
,
sizeof
(
sel
->
saddr
),
&
sel
->
saddr
,
abuf
,
sizeof
(
abuf
)),
sel
->
prefixlen_s
);
memset
(
abuf
,
'\0'
,
sizeof
(
abuf
));
fprintf
(
fp
,
" %s/%d"
,
rt_addr_n2a
(
f
,
sizeof
(
sel
->
daddr
),
&
sel
->
daddr
,
abuf
,
sizeof
(
abuf
)),
sel
->
prefixlen_d
);
fprintf
(
fp
,
"
\n
"
);
if
(
prefix
)
fprintf
(
fp
,
prefix
);
fprintf
(
fp
,
"
\t
"
);
fprintf
(
fp
,
"upspec %u %s(%u) "
,
sel
->
proto
,
mhname
,
type
);
break
;
}
#endif
default:
memset
(
abuf
,
'\0'
,
sizeof
(
abuf
));
fprintf
(
fp
,
"%s/%d[%u]"
,
rt_addr_n2a
(
f
,
sizeof
(
sel
->
saddr
),
&
sel
->
saddr
,
abuf
,
sizeof
(
abuf
)),
sel
->
prefixlen_s
,
sel
->
sport
);
memset
(
abuf
,
'\0'
,
sizeof
(
abuf
));
fprintf
(
fp
,
" %s/%d[%u]"
,
rt_addr_n2a
(
f
,
sizeof
(
sel
->
daddr
),
&
sel
->
daddr
,
abuf
,
sizeof
(
abuf
)),
sel
->
prefixlen_d
,
sel
->
dport
);
fprintf
(
fp
,
"
\n
"
);
if
(
prefix
)
fprintf
(
fp
,
prefix
);
fprintf
(
fp
,
"
\t
"
);
fprintf
(
fp
,
"upspec %u "
,
sel
->
proto
);
break
;
}
if
(
sel
->
ifindex
>
0
)
{
char
buf
[
IF_NAMESIZE
];
memset
(
buf
,
'\0'
,
sizeof
(
buf
));
if_indextoname
(
sel
->
ifindex
,
buf
);
fprintf
(
fp
,
"dev %s "
,
buf
);
}
else
fprintf
(
fp
,
"dev (none) "
);
fprintf
(
fp
,
"uid %u"
,
sel
->
user
);
fprintf
(
fp
,
"
\n
"
);
}
static
void
xfrm_algo_print
(
struct
xfrm_algo
*
algo
,
FILE
*
fp
,
const
char
*
prefix
)
{
int
len
;
int
i
;
if
(
prefix
)
fprintf
(
fp
,
prefix
);
fprintf
(
fp
,
"%s"
,
algo
->
alg_name
);
len
=
algo
->
alg_key_len
/
8
;
for
(
i
=
0
;
i
<
len
;
i
++
)
{
if
(
i
%
4
==
0
)
fprintf
(
fp
,
" "
);
fprintf
(
fp
,
"%x"
,
algo
->
alg_key
[
i
]);
}
fprintf
(
fp
,
"
\n
"
);
}
static
const
char
*
strxf_mask
(
__u32
mask
)
{
static
char
str
[
128
];
const
int
sn
=
sizeof
(
mask
)
*
8
-
1
;
__u32
b
;
int
finish
=
0
;
int
broken
=
0
;
int
i
=
0
;
for
(
b
=
(
1
<<
sn
);
b
>
0
;
b
>>=
1
)
{
if
((
b
&
mask
)
==
0
)
{
if
(
!
finish
)
finish
=
1
;
}
else
{
if
(
!
finish
)
i
++
;
else
{
broken
=
1
;
break
;
}
}
}
if
(
!
broken
)
sprintf
(
str
,
"%u"
,
i
);
else
sprintf
(
str
,
"broken(%u)"
,
mask
);
return
str
;
}
static
void
xfrm_tmpl_print
(
struct
xfrm_user_tmpl
*
tmpls
,
int
ntmpls
,
__u16
family
,
FILE
*
fp
,
const
char
*
prefix
)
{
char
buf
[
32
];
const
char
*
p
=
NULL
;
int
i
;
if
(
prefix
)
{
strcpy
(
buf
,
prefix
);
strcat
(
buf
,
" "
);
}
else
strcpy
(
buf
,
" "
);
p
=
buf
;
for
(
i
=
0
;
i
<
ntmpls
;
i
++
)
{
struct
xfrm_user_tmpl
*
tmpl
=
&
tmpls
[
i
];
if
(
prefix
)
fprintf
(
fp
,
prefix
);
fprintf
(
fp
,
"tmpl-%d:
\n
"
,
i
+
1
);
xfrm_id_info_print
(
&
tmpl
->
saddr
,
&
tmpl
->
id
,
tmpl
->
mode
,
tmpl
->
reqid
,
family
,
fp
,
p
);
fprintf
(
fp
,
p
);
fprintf
(
fp
,
"
\t
"
);
fprintf
(
fp
,
"level %s "
,
((
tmpl
->
optional
==
0
)
?
"required"
:
(
tmpl
->
optional
==
1
)
?
"use"
:
"unknown-level"
));
fprintf
(
fp
,
"share %s "
,
strxf_share
(
tmpl
->
share
));
fprintf
(
fp
,
"algo-mask:"
);
fprintf
(
fp
,
"E=%s, "
,
strxf_mask
(
tmpl
->
ealgos
));
fprintf
(
fp
,
"A=%s, "
,
strxf_mask
(
tmpl
->
aalgos
));
fprintf
(
fp
,
"C=%s"
,
strxf_mask
(
tmpl
->
calgos
));
fprintf
(
fp
,
"
\n
"
);
}
}
void
xfrm_xfrma_print
(
struct
rtattr
*
tb
[],
int
ntb
,
__u16
family
,
FILE
*
fp
,
const
char
*
prefix
)
{
int
i
;
for
(
i
=
0
;
i
<
ntb
;
i
++
)
{
__u16
type
=
tb
[
i
]
->
rta_type
;
void
*
data
=
RTA_DATA
(
tb
[
i
]);
switch
(
type
)
{
case
XFRMA_ALG_CRYPT
:
if
(
prefix
)
fprintf
(
fp
,
prefix
);
xfrm_algo_print
((
struct
xfrm_algo
*
)
data
,
fp
,
"E:"
);
break
;
case
XFRMA_ALG_AUTH
:
if
(
prefix
)
fprintf
(
fp
,
prefix
);
xfrm_algo_print
((
struct
xfrm_algo
*
)
data
,
fp
,
"A:"
);
break
;
case
XFRMA_ALG_COMP
:
if
(
prefix
)
fprintf
(
fp
,
prefix
);
xfrm_algo_print
((
struct
xfrm_algo
*
)
data
,
fp
,
"C:"
);
break
;
case
XFRMA_ENCAP
:
if
(
prefix
)
fprintf
(
fp
,
prefix
);
/* XXX */
fprintf
(
fp
,
"encap: (not implemented yet!)
\n
"
);
break
;
case
XFRMA_TMPL
:
{
int
len
=
tb
[
i
]
->
rta_len
;
int
ntmpls
=
len
/
sizeof
(
struct
xfrm_user_tmpl
);
xfrm_tmpl_print
((
struct
xfrm_user_tmpl
*
)
data
,
ntmpls
,
family
,
fp
,
prefix
);
break
;
}
#ifdef USE_MIP6
case
XFRMA_ADDR
:
{
char
abuf
[
256
];
xfrm_address_t
*
addr
=
(
xfrm_address_t
*
)
data
;
if
(
prefix
)
fprintf
(
fp
,
prefix
);
memset
(
abuf
,
'\0'
,
sizeof
(
abuf
));
fprintf
(
fp
,
"coa %s
\n
"
,
rt_addr_n2a
(
family
,
sizeof
(
*
addr
),
addr
,
abuf
,
sizeof
(
abuf
)));
break
;
}
#endif
default:
if
(
prefix
)
fprintf
(
fp
,
prefix
);
fprintf
(
fp
,
"unknown rta_type: %u
\n
"
,
type
);
break
;
}
}
}
int
xfrm_id_parse
(
xfrm_address_t
*
saddr
,
struct
xfrm_id
*
id
,
__u16
*
family
,
int
*
argcp
,
char
***
argvp
)
{
int
argc
=
*
argcp
;
char
**
argv
=
*
argvp
;
inet_prefix
dst
;
inet_prefix
src
;
__u8
proto
=
0
;
memset
(
&
dst
,
0
,
sizeof
(
dst
));
memset
(
&
src
,
0
,
sizeof
(
src
));
while
(
1
)
{
if
(
strcmp
(
*
argv
,
"src"
)
==
0
)
{
NEXT_ARG
();
get_prefix
(
&
src
,
*
argv
,
preferred_family
);
if
(
src
.
family
==
AF_UNSPEC
)
invarg
(
"
\"
SADDR
\"
address family is AF_UNSPEC"
,
*
argv
);
if
(
family
)
*
family
=
src
.
family
;
memcpy
(
saddr
,
&
src
.
data
,
sizeof
(
*
saddr
));
filter
.
id_src_mask
=
src
.
bitlen
;
}
else
if
(
strcmp
(
*
argv
,
"dst"
)
==
0
)
{
NEXT_ARG
();
get_prefix
(
&
dst
,
*
argv
,
preferred_family
);
if
(
dst
.
family
==
AF_UNSPEC
)
invarg
(
"
\"
DADDR
\"
address family is AF_UNSPEC"
,
*
argv
);
if
(
family
)
*
family
=
dst
.
family
;
memcpy
(
&
id
->
daddr
,
&
dst
.
data
,
sizeof
(
id
->
daddr
));
filter
.
id_dst_mask
=
dst
.
bitlen
;
}
else
if
(
strcmp
(
*
argv
,
"proto"
)
==
0
)
{
NEXT_ARG
();
#ifdef USE_MIP6
if
(
strcmp
(
*
argv
,
"route2"
)
==
0
)
proto
=
IPPROTO_ROUTING
;
else
if
(
strcmp
(
*
argv
,
"hao"
)
==
0
)
proto
=
IPPROTO_DSTOPTS
;
#else
if
(
0
)
;
#endif
else
{
struct
protoent
*
pp
;
pp
=
getprotobyname
(
*
argv
);
if
(
pp
)
proto
=
pp
->
p_proto
;
else
{
if
(
get_u8
(
&
proto
,
*
argv
,
0
))
invarg
(
"
\"
PROTO
\"
is invalid"
,
*
argv
);
}
}
switch
(
proto
)
{
case
IPPROTO_ESP
:
case
IPPROTO_AH
:
case
IPPROTO_COMP
:
#ifdef USE_MIP6
case
IPPROTO_ROUTING
:
case
IPPROTO_DSTOPTS
:
#endif
case
IPPROTO_IPV6
:
/* XXX: for testing */
id
->
proto
=
proto
;
break
;
default:
invarg
(
"
\"
PROTO
\"
is unsuppored proto"
,
*
argv
);
}
filter
.
id_proto_mask
=
XFRM_FILTER_MASK_FULL
;
}
else
if
(
strcmp
(
*
argv
,
"spi"
)
==
0
)
{
__u32
spi
;
NEXT_ARG
();
if
(
get_u32
(
&
spi
,
*
argv
,
0
))
invarg
(
"
\"
SPI
\"
is invalid"
,
*
argv
);
spi
=
htonl
(
spi
);
id
->
spi
=
spi
;
filter
.
id_spi_mask
=
XFRM_FILTER_MASK_FULL
;
}
else
{
PREV_ARG
();
/* back track */
break
;
}
if
(
!
NEXT_ARG_OK
())
break
;
NEXT_ARG
();
}
if
(
src
.
family
&&
dst
.
family
&&
(
src
.
family
!=
dst
.
family
))
invarg
(
"the same address family is required between
\"
SADDR
\"
and
\"
DADDR
\"
"
,
*
argv
);
if
(
proto
==
0
)
missarg
(
"PROTO"
);
if
(
argc
==
*
argcp
)
missarg
(
"ID"
);
*
argcp
=
argc
;
*
argvp
=
argv
;
return
0
;
}
int
xfrm_mode_parse
(
__u8
*
mode
,
int
*
argcp
,
char
***
argvp
)
{
int
argc
=
*
argcp
;
char
**
argv
=
*
argvp
;
if
(
matches
(
*
argv
,
"transport"
)
==
0
)
*
mode
=
0
;
else
if
(
matches
(
*
argv
,
"tunnel"
)
==
0
)
*
mode
=
1
;
else
invarg
(
"
\"
MODE
\"
is invalid"
,
*
argv
);
*
argcp
=
argc
;
*
argvp
=
argv
;
return
0
;
}
/* NOTE: reqid is used by host-byte order */
int
xfrm_reqid_parse
(
__u32
*
reqid
,
int
*
argcp
,
char
***
argvp
)
{
int
argc
=
*
argcp
;
char
**
argv
=
*
argvp
;
if
(
get_u32
(
reqid
,
*
argv
,
0
))
invarg
(
"
\"
REQID
\"
is invalid"
,
*
argv
);
*
argcp
=
argc
;
*
argvp
=
argv
;
return
0
;
}
static
int
xfrm_selector_upspec_parse
(
struct
xfrm_selector
*
sel
,
int
*
argcp
,
char
***
argvp
)
{
int
argc
=
*
argcp
;
char
**
argv
=
*
argvp
;
__u8
upspec
;
while
(
1
)
{
if
(
strcmp
(
*
argv
,
"proto"
)
==
0
)
{
NEXT_ARG
();
if
(
strcmp
(
*
argv
,
"any"
)
==
0
)
upspec
=
0
;
else
{
struct
protoent
*
pp
;
pp
=
getprotobyname
(
*
argv
);
if
(
pp
)
upspec
=
pp
->
p_proto
;
else
{
if
(
get_u8
(
&
upspec
,
*
argv
,
0
))
invarg
(
"
\"
UPSPEC
\"
is invalid"
,
*
argv
);
}
}
sel
->
proto
=
upspec
;
filter
.
upspec_proto_mask
=
XFRM_FILTER_MASK_FULL
;
}
else
if
(
strcmp
(
*
argv
,
"sport"
)
==
0
)
{
if
(
sel
->
proto
==
IPPROTO_ICMPV6
||
sel
->
proto
==
IPPROTO_MH
)
{
char
buf
[
32
];
sprintf
(
buf
,
"
\"
sport
\"
is invalid with upspec=%d"
,
sel
->
proto
);
invarg
(
buf
,
*
argv
);
}
NEXT_ARG
();
if
(
get_u16
(
&
sel
->
sport
,
*
argv
,
0
))
invarg
(
"
\"
PORT
\"
is invalid"
,
*
argv
);
sel
->
sport
=
htons
(
sel
->
sport
);
if
(
sel
->
sport
)
sel
->
sport_mask
=
~
((
__u16
)
0
);
filter
.
upspec_sport_mask
=
XFRM_FILTER_MASK_FULL
;
}
else
if
(
strcmp
(
*
argv
,
"dport"
)
==
0
)
{
if
(
sel
->
proto
==
IPPROTO_ICMPV6
||
sel
->
proto
==
IPPROTO_MH
)
{
char
buf
[
32
];
sprintf
(
buf
,
"
\"
dport
\"
is invalid with upspec=%d"
,
sel
->
proto
);
invarg
(
buf
,
*
argv
);
}
NEXT_ARG
();
if
(
get_u16
(
&
sel
->
dport
,
*
argv
,
0
))
invarg
(
"
\"
PORT
\"
is invalid"
,
*
argv
);
sel
->
dport
=
htons
(
sel
->
dport
);
if
(
sel
->
dport
)
sel
->
dport_mask
=
~
((
__u16
)
0
);
filter
.
upspec_dport_mask
=
XFRM_FILTER_MASK_FULL
;
}
else
if
(
strcmp
(
*
argv
,
"type"
)
==
0
)
{
if
(
sel
->
proto
==
IPPROTO_ICMPV6
)
{
NEXT_ARG
();
if
(
get_u16
(
&
sel
->
xfrmsel_icmp_type
,
*
argv
,
0
))
invarg
(
"
\"
TYPE
\"
is invalid"
,
*
argv
);
sel
->
xfrmsel_icmp_type
=
htons
(
sel
->
xfrmsel_icmp_type
);
if
(
sel
->
xfrmsel_icmp_type
)
sel
->
xfrmsel_icmp_type_mask
=
~
((
__u16
)
0
);
filter
.
upspec_type_mask
=
XFRM_FILTER_MASK_FULL
;
}
else
if
(
sel
->
proto
==
IPPROTO_MH
)
{
#ifdef USE_MIP6
NEXT_ARG
();
if
(
strcmp
(
*
argv
,
"any"
)
==
0
)
{
sel
->
xfrmsel_mh_type
=
0
;
sel
->
xfrmsel_mh_type_mask
=
0
;
}
else
{
const
struct
typeent
*
tp
;
tp
=
getmhtypebyname
(
*
argv
);
if
(
tp
)
{
sel
->
xfrmsel_mh_type
=
tp
->
t_type
;
sel
->
xfrmsel_mh_type
=
htons
(
sel
->
xfrmsel_mh_type
);
}
else
{
if
(
get_u16
(
&
sel
->
xfrmsel_mh_type
,
*
argv
,
0
))
invarg
(
"
\"
TYPE
\"
is invalid"
,
*
argv
);
sel
->
xfrmsel_mh_type
=
htons
(
sel
->
xfrmsel_mh_type
);
}
/* mask is filled if user specified type */
sel
->
xfrmsel_mh_type_mask
=
~
((
__u16
)
0
);
#else
invarg
(
"sorry,
\"
type
\"
is not supported for ipv6-mh"
,
*
argv
);
#endif
}
filter
.
upspec_type_mask
=
XFRM_FILTER_MASK_FULL
;
}
else
{
char
buf
[
32
];
sprintf
(
buf
,
"
\"
type
\"
is invalid with upspec=%d"
,
sel
->
proto
);
invarg
(
buf
,
*
argv
);
}
}
else
if
(
strcmp
(
*
argv
,
"code"
)
==
0
)
{
if
(
sel
->
proto
!=
IPPROTO_ICMPV6
)
{
char
buf
[
32
];
sprintf
(
buf
,
"
\"
code
\"
is invalid with upspec=%d"
,
sel
->
proto
);
invarg
(
buf
,
*
argv
);
}
if
(
get_u16
(
&
sel
->
xfrmsel_icmp_code
,
*
argv
,
0
))
invarg
(
"
\"
CODE
\"
is invalid"
,
*
argv
);
sel
->
xfrmsel_icmp_code
=
htons
(
sel
->
xfrmsel_icmp_code
);
if
(
sel
->
xfrmsel_icmp_code
)
sel
->
xfrmsel_icmp_code_mask
=
~
((
__u16
)
0
);
filter
.
upspec_code_mask
=
XFRM_FILTER_MASK_FULL
;
}
else
{
PREV_ARG
();
/* back track */
break
;
}
if
(
!
NEXT_ARG_OK
())
break
;
NEXT_ARG
();
}
if
(
argc
==
*
argcp
)
missarg
(
"UPSPEC"
);
*
argcp
=
argc
;
*
argvp
=
argv
;
return
0
;
}
int
xfrm_selector_parse
(
struct
xfrm_selector
*
sel
,
int
*
argcp
,
char
***
argvp
)
{
int
argc
=
*
argcp
;
char
**
argv
=
*
argvp
;
inet_prefix
dst
;
inet_prefix
src
;
memset
(
&
dst
,
0
,
sizeof
(
dst
));
memset
(
&
src
,
0
,
sizeof
(
src
));
while
(
1
)
{
if
(
strcmp
(
*
argv
,
"src"
)
==
0
)
{
NEXT_ARG
();
get_prefix
(
&
src
,
*
argv
,
preferred_family
);
if
(
src
.
family
==
AF_UNSPEC
)
invarg
(
"
\"
SADDR
\"
address family is AF_UNSPEC"
,
*
argv
);
sel
->
family
=
src
.
family
;
memcpy
(
&
sel
->
saddr
,
&
src
.
data
,
sizeof
(
sel
->
saddr
));
sel
->
prefixlen_s
=
src
.
bitlen
;
filter
.
sel_src_mask
=
src
.
bitlen
;
}
else
if
(
strcmp
(
*
argv
,
"dst"
)
==
0
)
{
NEXT_ARG
();
get_prefix
(
&
dst
,
*
argv
,
preferred_family
);
if
(
dst
.
family
==
AF_UNSPEC
)
invarg
(
"
\"
DADDR
\"
address family is AF_UNSPEC"
,
*
argv
);
sel
->
family
=
dst
.
family
;
memcpy
(
&
sel
->
daddr
,
&
dst
.
data
,
sizeof
(
sel
->
daddr
));
sel
->
prefixlen_d
=
dst
.
bitlen
;
filter
.
sel_dst_mask
=
dst
.
bitlen
;
}
else
if
(
strcmp
(
*
argv
,
"upspec"
)
==
0
)
{
NEXT_ARG
();
xfrm_selector_upspec_parse
(
sel
,
&
argc
,
&
argv
);
}
else
if
(
strcmp
(
*
argv
,
"dev"
)
==
0
)
{
int
ifindex
;
NEXT_ARG
();
if
(
strcmp
(
*
argv
,
"none"
)
==
0
)
ifindex
=
0
;
else
{
ifindex
=
if_nametoindex
(
*
argv
);
if
(
ifindex
<=
0
)
invarg
(
"
\"
DEV
\"
is invalid"
,
*
argv
);
}
sel
->
ifindex
=
ifindex
;
filter
.
sel_dev_mask
=
XFRM_FILTER_MASK_FULL
;
}
else
{
PREV_ARG
();
/* back track */
break
;
}
if
(
!
NEXT_ARG_OK
())
break
;
NEXT_ARG
();
}
if
(
src
.
family
&&
dst
.
family
&&
(
src
.
family
!=
dst
.
family
))
invarg
(
"the same address family is required between
\"
SADDR
\"
and
\"
DADDR
\"
"
,
*
argv
);
if
(
argc
==
*
argcp
)
missarg
(
"SELECTOR"
);
*
argcp
=
argc
;
*
argvp
=
argv
;
return
0
;
}
int
xfrm_lifetime_cfg_parse
(
struct
xfrm_lifetime_cfg
*
lft
,
int
*
argcp
,
char
***
argvp
)
{
int
argc
=
*
argcp
;
char
**
argv
=
*
argvp
;
int
ret
;
if
(
strcmp
(
*
argv
,
"time-soft"
)
==
0
)
{
NEXT_ARG
();
ret
=
get_u64
(
&
lft
->
soft_add_expires_seconds
,
*
argv
,
0
);
if
(
ret
)
invarg
(
"
\"
time-soft
\"
value is invalid"
,
*
argv
);
}
else
if
(
strcmp
(
*
argv
,
"time-hard"
)
==
0
)
{
NEXT_ARG
();
ret
=
get_u64
(
&
lft
->
hard_add_expires_seconds
,
*
argv
,
0
);
if
(
ret
)
invarg
(
"
\"
time-hard
\"
value is invalid"
,
*
argv
);
}
else
if
(
strcmp
(
*
argv
,
"time-use-soft"
)
==
0
)
{
NEXT_ARG
();
ret
=
get_u64
(
&
lft
->
soft_use_expires_seconds
,
*
argv
,
0
);
if
(
ret
)
invarg
(
"
\"
time-use-soft
\"
value is invalid"
,
*
argv
);
}
else
if
(
strcmp
(
*
argv
,
"time-use-hard"
)
==
0
)
{
NEXT_ARG
();
ret
=
get_u64
(
&
lft
->
hard_use_expires_seconds
,
*
argv
,
0
);
if
(
ret
)
invarg
(
"
\"
time-use-hard
\"
value is invalid"
,
*
argv
);
}
else
if
(
strcmp
(
*
argv
,
"byte-soft"
)
==
0
)
{
NEXT_ARG
();
ret
=
get_u64
(
&
lft
->
soft_byte_limit
,
*
argv
,
0
);
if
(
ret
)
invarg
(
"
\"
byte-soft
\"
value is invalid"
,
*
argv
);
}
else
if
(
strcmp
(
*
argv
,
"byte-hard"
)
==
0
)
{
NEXT_ARG
();
ret
=
get_u64
(
&
lft
->
hard_byte_limit
,
*
argv
,
0
);
if
(
ret
)
invarg
(
"
\"
byte-hard
\"
value is invalid"
,
*
argv
);
}
else
if
(
strcmp
(
*
argv
,
"packet-soft"
)
==
0
)
{
NEXT_ARG
();
ret
=
get_u64
(
&
lft
->
soft_packet_limit
,
*
argv
,
0
);
if
(
ret
)
invarg
(
"
\"
packet-soft
\"
value is invalid"
,
*
argv
);
}
else
if
(
strcmp
(
*
argv
,
"packet-hard"
)
==
0
)
{
NEXT_ARG
();
ret
=
get_u64
(
&
lft
->
hard_packet_limit
,
*
argv
,
0
);
if
(
ret
)
invarg
(
"
\"
packet-hard
\"
value is invalid"
,
*
argv
);
}
else
invarg
(
"
\"
LIMIT
\"
is invalid"
,
*
argv
);
*
argcp
=
argc
;
*
argvp
=
argv
;
return
0
;
}
int
do_xfrm
(
int
argc
,
char
**
argv
)
{
memset
(
&
filter
,
0
,
sizeof
(
filter
));
if
(
argc
<
1
)
usage
();
if
(
strcmp
(
*
argv
,
"state"
)
==
0
||
strcmp
(
*
argv
,
"sa"
)
==
0
)
{
return
do_xfrm_state
(
argc
-
1
,
argv
+
1
);
}
else
if
(
strcmp
(
*
argv
,
"policy"
)
==
0
||
strcmp
(
*
argv
,
"pol"
)
==
0
)
{
return
do_xfrm_policy
(
argc
-
1
,
argv
+
1
);
}
else
if
(
strcmp
(
*
argv
,
"help"
)
==
0
)
{
usage
();
fprintf
(
stderr
,
"xfrm Object
\"
%s
\"
is unknown.
\n
"
,
*
argv
);
exit
(
-
1
);
}
usage
();
}
iproute2/ip/xfrm.h
View file @
9111bd8e
/* $USAGI: $ */
/*
* Copyright (C)2004 USAGI/WIDE Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Authors:
* Masahide NAKAMURA @USAGI
*/
#ifndef __XFRM_H__
#define __XFRM_H__ 1
#include <stdio.h>
#include <sys/socket.h>
#include <linux/xfrm.h>
#include "utils.h"
#define XFRM_MAX_DEPTH 6
#define XFRMS_RTA(x) ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_usersa_info))))
#define XFRMS_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct xfrm_usersa_info))
#define XFRMP_RTA(x) ((struct rtattr*)(((char*)(x)) + NLMSG_ALIGN(sizeof(struct xfrm_userpolicy_info))))
#define XFRMP_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct xfrm_userpoilcy_info))
struct
xfrm_buffer
{
char
*
buf
;
int
size
;
int
offset
;
int
nlmsg_count
;
struct
rtnl_handle
*
rth
;
};
struct
xfrm_filter
{
int
use
;
struct
xfrm_usersa_info
xsinfo
;
__u32
id_src_mask
;
__u32
id_dst_mask
;
__u32
id_proto_mask
;
__u32
id_spi_mask
;
__u32
mode_mask
;
__u32
reqid_mask
;
__u32
state_flags_mask
;
struct
xfrm_userpolicy_info
xpinfo
;
__u32
dir_mask
;
__u32
sel_src_mask
;
__u32
sel_dst_mask
;
__u32
sel_dev_mask
;
__u32
upspec_proto_mask
;
__u32
upspec_sport_mask
;
__u32
upspec_dport_mask
;
__u32
upspec_type_mask
;
__u32
upspec_code_mask
;
__u32
index_mask
;
__u32
action_mask
;
__u32
priority_mask
;
};
#define XFRM_FILTER_MASK_FULL (~(__u32)0)
extern
struct
xfrm_filter
filter
;
int
do_xfrm_state
(
int
argc
,
char
**
argv
);
int
do_xfrm_policy
(
int
argc
,
char
**
argv
);
const
char
*
strxf_flags
(
__u8
flags
);
const
char
*
strxf_share
(
__u8
share
);
void
xfrm_id_info_print
(
xfrm_address_t
*
saddr
,
struct
xfrm_id
*
id
,
__u8
mode
,
__u32
reqid
,
__u16
family
,
FILE
*
fp
,
const
char
*
prefix
);
void
xfrm_stats_print
(
struct
xfrm_stats
*
s
,
FILE
*
fp
,
const
char
*
prefix
);
void
xfrm_lifetime_print
(
struct
xfrm_lifetime_cfg
*
cfg
,
struct
xfrm_lifetime_cur
*
cur
,
FILE
*
fp
,
const
char
*
prefix
);
void
xfrm_selector_print
(
struct
xfrm_selector
*
sel
,
__u16
family
,
FILE
*
fp
,
const
char
*
prefix
);
void
xfrm_xfrma_print
(
struct
rtattr
*
tb
[],
int
ntb
,
__u16
family
,
FILE
*
fp
,
const
char
*
prefix
);
int
xfrm_id_parse
(
xfrm_address_t
*
saddr
,
struct
xfrm_id
*
id
,
__u16
*
family
,
int
*
argcp
,
char
***
argvp
);
int
xfrm_mode_parse
(
__u8
*
mode
,
int
*
argcp
,
char
***
argvp
);
int
xfrm_reqid_parse
(
__u32
*
reqid
,
int
*
argcp
,
char
***
argvp
);
int
xfrm_selector_parse
(
struct
xfrm_selector
*
sel
,
int
*
argcp
,
char
***
argvp
);
int
xfrm_lifetime_cfg_parse
(
struct
xfrm_lifetime_cfg
*
lft
,
int
*
argcp
,
char
***
argvp
);
#endif
iproute2/ip/xfrm_policy.c
View file @
9111bd8e
/* $USAGI: $ */
/*
* Copyright (C)2004 USAGI/WIDE Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* based on iproute.c
*/
/*
* Authors:
* Masahide NAKAMURA @USAGI
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <linux/netlink.h>
#include <linux/xfrm.h>
#include "utils.h"
#include "xfrm.h"
#include "ip_common.h"
//#define NLMSG_FLUSH_BUF_SIZE (4096-512)
#define NLMSG_FLUSH_BUF_SIZE 8192
/*
* Receiving buffer defines:
* nlmsg
* data = struct xfrm_userpolicy_info
* rtattr
* data = struct xfrm_user_tmpl[]
*/
#define NLMSG_BUF_SIZE 4096
#define RTA_BUF_SIZE 2048
#define XFRM_TMPLS_BUF_SIZE 1024
static
void
usage
(
void
)
__attribute__
((
noreturn
));
static
void
usage
(
void
)
{
fprintf
(
stderr
,
"Usage: ip xfrm policy { add | update } dir DIR sel SELECTOR [ index INDEX ]
\n
"
);
fprintf
(
stderr
,
" [ action ACTION ] [ priority PRIORITY ] [ LIMIT-LIST ] [ TMPL-LIST ]
\n
"
);
fprintf
(
stderr
,
"Usage: ip xfrm policy { merge | change | replace } dir DIR
\n
"
);
fprintf
(
stderr
,
" [ sel SELECTOR | index INDEX ] [ TMPL-LIST ]
\n
"
);
fprintf
(
stderr
,
"Usage: ip xfrm policy { delete | get } dir DIR [ sel SELECTOR | index INDEX ]
\n
"
);
fprintf
(
stderr
,
"Usage: ip xfrm policy { flush | list } [ dir DIR ] [ sel SELECTOR ]
\n
"
);
fprintf
(
stderr
,
" [ index INDEX ] [ action ACTION ] [ priority PRIORITY ]
\n
"
);
fprintf
(
stderr
,
"DIR := [ in | out | fwd ]
\n
"
);
fprintf
(
stderr
,
"SELECTOR := src ADDR[/PLEN] dst ADDR[/PLEN] [ upspec UPSPEC ] [ dev DEV ]
\n
"
);
fprintf
(
stderr
,
"UPSPEC := proto PROTO [ UPSPEC_OPT ]
\n
"
);
fprintf
(
stderr
,
"UPSPEC_OPT := [ [ sport PORT ] [ dport PORT ] ] |
\n
"
);
#ifdef USE_MIP6
fprintf
(
stderr
,
" [ type TYPE [ code CODE ] ](for PROTO=ipv6-icmp) |
\n
"
);
fprintf
(
stderr
,
" [ type TYPE ](for PROTO=ipv6-mh)
\n
"
);
#else
fprintf
(
stderr
,
" [ type TYPE [ code CODE ] ](for PROTO=ipv6-icmp)
\n
"
);
#endif
//fprintf(stderr, "DEV - device name(default=none)\n");
fprintf
(
stderr
,
"ACTION := [ allow | block ](default=allow)
\n
"
);
//fprintf(stderr, "PRIORITY - priority value(default=0)\n");
fprintf
(
stderr
,
"LIMIT-LIST := [ LIMIT-LIST ] | [ limit LIMIT ]
\n
"
);
fprintf
(
stderr
,
"LIMIT := [ [time-soft|time-hard|time-use-soft|time-use-hard] SECONDS ] |
\n
"
);
fprintf
(
stderr
,
" [ [byte-soft|byte-hard] SIZE ] | [ [packet-soft|packet-hard] NUMBER ]
\n
"
);
fprintf
(
stderr
,
"TMPL-LIST := [ TMPL-LIST ] | [ tmpl TMPL ] | [ tmpl remain ](change only)
\n
"
);
fprintf
(
stderr
,
"TMPL := ID [ mode MODE ] [ reqid REQID ] [ level LEVEL ]
\n
"
);
fprintf
(
stderr
,
"ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM_PROTO ] [ spi SPI ]
\n
"
);
#ifdef USE_MIP6
fprintf
(
stderr
,
"XFRM_PROTO := [ esp | ah | ipcomp | route2 | hao ]
\n
"
);
#else
fprintf
(
stderr
,
"XFRM_PROTO := [ esp | ah | ipcomp ]
\n
"
);
#endif
fprintf
(
stderr
,
"MODE := [ transport | tunnel ](default=transport)
\n
"
);
//fprintf(stderr, "REQID - number(default=0)\n");
fprintf
(
stderr
,
"LEVEL := [ required | use ](default=required)
\n
"
);
exit
(
-
1
);
}
static
int
xfrm_policy_dir_parse
(
__u8
*
dir
,
int
*
argcp
,
char
***
argvp
)
{
int
argc
=
*
argcp
;
char
**
argv
=
*
argvp
;
if
(
strcmp
(
*
argv
,
"in"
)
==
0
)
*
dir
=
XFRM_POLICY_IN
;
else
if
(
strcmp
(
*
argv
,
"out"
)
==
0
)
*
dir
=
XFRM_POLICY_OUT
;
else
if
(
strcmp
(
*
argv
,
"fwd"
)
==
0
)
*
dir
=
XFRM_POLICY_FWD
;
else
invarg
(
"
\"
DIR
\"
is invalid"
,
*
argv
);
*
argcp
=
argc
;
*
argvp
=
argv
;
return
0
;
}
static
int
xfrm_tmpl_parse
(
struct
xfrm_user_tmpl
*
tmpl
,
int
*
argcp
,
char
***
argvp
)
{
int
argc
=
*
argcp
;
char
**
argv
=
*
argvp
;
char
*
idp
=
NULL
;
while
(
1
)
{
if
(
strcmp
(
*
argv
,
"mode"
)
==
0
)
{
NEXT_ARG
();
xfrm_mode_parse
(
&
tmpl
->
mode
,
&
argc
,
&
argv
);
}
else
if
(
strcmp
(
*
argv
,
"reqid"
)
==
0
)
{
NEXT_ARG
();
xfrm_reqid_parse
(
&
tmpl
->
reqid
,
&
argc
,
&
argv
);
}
else
if
(
strcmp
(
*
argv
,
"level"
)
==
0
)
{
NEXT_ARG
();
if
(
strcmp
(
*
argv
,
"required"
)
==
0
)
tmpl
->
optional
=
0
;
else
if
(
strcmp
(
*
argv
,
"use"
)
==
0
)
tmpl
->
optional
=
1
;
else
invarg
(
"
\"
level
\"
value is invalid
\n
"
,
*
argv
);
}
else
{
if
(
idp
)
{
PREV_ARG
();
/* back track */
break
;
}
idp
=
*
argv
;
xfrm_id_parse
(
&
tmpl
->
saddr
,
&
tmpl
->
id
,
&
tmpl
->
family
,
&
argc
,
&
argv
);
if
(
preferred_family
==
AF_UNSPEC
)
preferred_family
=
tmpl
->
family
;
}
if
(
!
NEXT_ARG_OK
())
break
;
NEXT_ARG
();
}
if
(
argc
==
*
argcp
)
missarg
(
"TMPL"
);
*
argcp
=
argc
;
*
argvp
=
argv
;
return
0
;
}
static
int
xfrm_policy_modify
(
int
cmd
,
unsigned
flags
,
int
argc
,
char
**
argv
)
{
struct
rtnl_handle
rth
;
struct
{
struct
nlmsghdr
n
;
struct
xfrm_userpolicy_info
xpinfo
;
char
buf
[
RTA_BUF_SIZE
];
}
req
;
char
*
dirp
=
NULL
;
char
tmpls_buf
[
XFRM_TMPLS_BUF_SIZE
];
int
tmpls_len
=
0
;
memset
(
&
req
,
0
,
sizeof
(
req
));
memset
(
&
tmpls_buf
,
0
,
sizeof
(
tmpls_buf
));
req
.
n
.
nlmsg_len
=
NLMSG_LENGTH
(
sizeof
(
req
.
xpinfo
));
req
.
n
.
nlmsg_flags
=
NLM_F_REQUEST
|
flags
;
req
.
n
.
nlmsg_type
=
cmd
;
req
.
xpinfo
.
sel
.
family
=
preferred_family
;
req
.
xpinfo
.
lft
.
soft_byte_limit
=
XFRM_INF
;
req
.
xpinfo
.
lft
.
hard_byte_limit
=
XFRM_INF
;
req
.
xpinfo
.
lft
.
soft_packet_limit
=
XFRM_INF
;
req
.
xpinfo
.
lft
.
hard_packet_limit
=
XFRM_INF
;
while
(
argc
>
0
)
{
if
(
strcmp
(
*
argv
,
"dir"
)
==
0
)
{
if
(
dirp
)
duparg
(
"dir"
,
*
argv
);
dirp
=
*
argv
;
NEXT_ARG
();
xfrm_policy_dir_parse
(
&
req
.
xpinfo
.
dir
,
&
argc
,
&
argv
);
filter
.
dir_mask
=
XFRM_FILTER_MASK_FULL
;
}
else
if
(
strcmp
(
*
argv
,
"sel"
)
==
0
)
{
NEXT_ARG
();
xfrm_selector_parse
(
&
req
.
xpinfo
.
sel
,
&
argc
,
&
argv
);
if
(
preferred_family
==
AF_UNSPEC
)
preferred_family
=
req
.
xpinfo
.
sel
.
family
;
}
else
if
(
strcmp
(
*
argv
,
"index"
)
==
0
)
{
NEXT_ARG
();
if
(
get_u32
(
&
req
.
xpinfo
.
index
,
*
argv
,
0
))
invarg
(
"
\"
INDEX
\"
is invalid"
,
*
argv
);
filter
.
index_mask
=
XFRM_FILTER_MASK_FULL
;
}
else
if
(
strcmp
(
*
argv
,
"action"
)
==
0
)
{
NEXT_ARG
();
if
(
strcmp
(
*
argv
,
"allow"
)
==
0
)
req
.
xpinfo
.
action
=
XFRM_POLICY_ALLOW
;
else
if
(
strcmp
(
*
argv
,
"block"
)
==
0
)
req
.
xpinfo
.
action
=
XFRM_POLICY_BLOCK
;
else
invarg
(
"
\"
action
\"
value is invalid
\n
"
,
*
argv
);
filter
.
action_mask
=
XFRM_FILTER_MASK_FULL
;
}
else
if
(
strcmp
(
*
argv
,
"priority"
)
==
0
)
{
NEXT_ARG
();
if
(
get_u32
(
&
req
.
xpinfo
.
priority
,
*
argv
,
0
))
invarg
(
"
\"
PRIORITY
\"
is invalid"
,
*
argv
);
filter
.
priority_mask
=
XFRM_FILTER_MASK_FULL
;
}
else
if
(
strcmp
(
*
argv
,
"limit"
)
==
0
)
{
NEXT_ARG
();
xfrm_lifetime_cfg_parse
(
&
req
.
xpinfo
.
lft
,
&
argc
,
&
argv
);
}
else
if
(
strcmp
(
*
argv
,
"tmpl"
)
==
0
)
{
struct
xfrm_user_tmpl
*
tmpl
;
if
(
tmpls_len
+
sizeof
(
*
tmpl
)
>
sizeof
(
tmpls_buf
))
{
fprintf
(
stderr
,
"Too many tmpls: buffer overflow
\n
"
);
exit
(
1
);
}
tmpl
=
(
struct
xfrm_user_tmpl
*
)((
char
*
)
tmpls_buf
+
tmpls_len
);
tmpl
->
family
=
preferred_family
;
tmpl
->
aalgos
=
(
~
(
__u32
)
0
);
tmpl
->
ealgos
=
(
~
(
__u32
)
0
);
tmpl
->
calgos
=
(
~
(
__u32
)
0
);
NEXT_ARG
();
xfrm_tmpl_parse
(
tmpl
,
&
argc
,
&
argv
);
tmpls_len
+=
sizeof
(
*
tmpl
);
}
else
invarg
(
"unknown"
,
*
argv
);
argc
--
;
argv
++
;
}
if
(
!
dirp
)
{
fprintf
(
stderr
,
"Not enough information:
\"
DIR
\"
is required.
\n
"
);
exit
(
1
);
}
if
(
tmpls_len
>
0
)
{
addattr_l
(
&
req
.
n
,
sizeof
(
req
),
XFRMA_TMPL
,
(
void
*
)
tmpls_buf
,
tmpls_len
);
}
if
(
rtnl_open_byproto
(
&
rth
,
0
,
NETLINK_XFRM
)
<
0
)
exit
(
1
);
if
(
req
.
xpinfo
.
sel
.
family
==
AF_UNSPEC
)
req
.
xpinfo
.
sel
.
family
=
AF_INET
;
if
(
rtnl_talk
(
&
rth
,
&
req
.
n
,
0
,
0
,
NULL
,
NULL
,
NULL
)
<
0
)
exit
(
2
);
rtnl_close
(
&
rth
);
return
0
;
}
static
int
xfrm_policy_filter_match
(
struct
xfrm_userpolicy_info
*
xpinfo
)
{
if
(
!
filter
.
use
)
return
1
;
if
((
xpinfo
->
dir
^
filter
.
xpinfo
.
dir
)
&
filter
.
dir_mask
)
return
0
;
if
(
filter
.
sel_src_mask
)
{
if
(
memcmp
(
&
xpinfo
->
sel
.
saddr
,
&
filter
.
xpinfo
.
sel
.
saddr
,
filter
.
sel_src_mask
)
!=
0
)
return
0
;
if
(
xpinfo
->
sel
.
prefixlen_s
!=
filter
.
xpinfo
.
sel
.
prefixlen_s
)
return
0
;
}
if
(
filter
.
sel_dst_mask
)
{
if
(
memcmp
(
&
xpinfo
->
sel
.
daddr
,
&
filter
.
xpinfo
.
sel
.
daddr
,
filter
.
sel_dst_mask
)
!=
0
)
return
0
;
if
(
xpinfo
->
sel
.
prefixlen_d
!=
filter
.
xpinfo
.
sel
.
prefixlen_d
)
return
0
;
}
if
((
xpinfo
->
sel
.
ifindex
^
filter
.
xpinfo
.
sel
.
ifindex
)
&
filter
.
sel_dev_mask
)
return
0
;
if
((
xpinfo
->
sel
.
proto
^
filter
.
xpinfo
.
sel
.
proto
)
&
filter
.
upspec_proto_mask
)
return
0
;
if
(
filter
.
upspec_sport_mask
)
{
if
(
xpinfo
->
sel
.
proto
==
IPPROTO_ICMPV6
)
return
0
;
#ifdef USE_MIP6
if
(
xpinfo
->
sel
.
proto
==
IPPROTO_MH
)
return
0
;
#endif
if
((
xpinfo
->
sel
.
sport
^
filter
.
xpinfo
.
sel
.
sport
)
&
filter
.
upspec_sport_mask
)
return
0
;
}
if
(
filter
.
upspec_dport_mask
)
{
if
(
xpinfo
->
sel
.
proto
==
IPPROTO_ICMPV6
)
return
0
;
#ifdef USE_MIP6
if
(
xpinfo
->
sel
.
proto
==
IPPROTO_MH
)
return
0
;
#endif
if
((
xpinfo
->
sel
.
dport
^
filter
.
xpinfo
.
sel
.
dport
)
&
filter
.
upspec_dport_mask
)
return
0
;
}
if
(
filter
.
upspec_type_mask
)
{
if
(
xpinfo
->
sel
.
proto
==
IPPROTO_ICMPV6
)
{
if
((
xpinfo
->
sel
.
xfrmsel_icmp_type
^
filter
.
xpinfo
.
sel
.
xfrmsel_icmp_type
)
&
filter
.
xpinfo
.
sel
.
xfrmsel_icmp_type_mask
)
return
0
;
#ifdef USE_MIP6
}
else
if
(
xpinfo
->
sel
.
proto
==
IPPROTO_MH
)
{
if
((
xpinfo
->
sel
.
xfrmsel_mh_type
^
filter
.
xpinfo
.
sel
.
xfrmsel_mh_type
)
&
filter
.
xpinfo
.
sel
.
xfrmsel_mh_type_mask
)
return
0
;
#endif
}
else
return
0
;
}
if
(
filter
.
upspec_code_mask
)
{
if
(
xpinfo
->
sel
.
proto
==
IPPROTO_ICMPV6
)
{
if
((
xpinfo
->
sel
.
xfrmsel_icmp_code
^
filter
.
xpinfo
.
sel
.
xfrmsel_icmp_code
)
&
filter
.
upspec_code_mask
)
return
0
;
}
else
return
0
;
}
if
((
xpinfo
->
index
^
filter
.
xpinfo
.
index
)
&
filter
.
index_mask
)
return
0
;
if
((
xpinfo
->
action
^
filter
.
xpinfo
.
action
)
&
filter
.
action_mask
)
return
0
;
if
((
xpinfo
->
priority
^
filter
.
xpinfo
.
priority
)
&
filter
.
priority_mask
)
return
0
;
return
1
;
}
int
xfrm_policy_print
(
struct
sockaddr_nl
*
who
,
struct
nlmsghdr
*
n
,
void
*
arg
)
{
FILE
*
fp
=
(
FILE
*
)
arg
;
struct
xfrm_userpolicy_info
*
xpinfo
=
NLMSG_DATA
(
n
);
int
len
=
n
->
nlmsg_len
;
struct
rtattr
*
tb
[
XFRM_MAX_DEPTH
];
int
ntb
;
if
(
n
->
nlmsg_type
!=
XFRM_MSG_NEWPOLICY
&&
n
->
nlmsg_type
!=
XFRM_MSG_DELPOLICY
)
{
fprintf
(
stderr
,
"Not a policy: %08x %08x %08x
\n
"
,
n
->
nlmsg_len
,
n
->
nlmsg_type
,
n
->
nlmsg_flags
);
return
0
;
}
len
-=
NLMSG_LENGTH
(
sizeof
(
*
xpinfo
));
if
(
len
<
0
)
{
fprintf
(
stderr
,
"BUG: wrong nlmsg len %d
\n
"
,
len
);
return
-
1
;
}
if
(
!
xfrm_policy_filter_match
(
xpinfo
))
return
0
;
memset
(
tb
,
0
,
sizeof
(
tb
));
ntb
=
parse_rtattr_byindex
(
tb
,
XFRM_MAX_DEPTH
,
XFRMP_RTA
(
xpinfo
),
len
);
if
(
n
->
nlmsg_type
==
XFRM_MSG_DELPOLICY
)
fprintf
(
fp
,
"Deleted "
);
xfrm_selector_print
(
&
xpinfo
->
sel
,
preferred_family
,
fp
,
NULL
);
fprintf
(
fp
,
"
\t
"
);
fprintf
(
fp
,
"%s "
,
(
xpinfo
->
dir
==
XFRM_POLICY_IN
?
"in "
:
xpinfo
->
dir
==
XFRM_POLICY_OUT
?
"out"
:
xpinfo
->
dir
==
XFRM_POLICY_FWD
?
"fwd"
:
"unknown-dir"
));
fprintf
(
fp
,
"%s "
,
(
xpinfo
->
action
==
XFRM_POLICY_ALLOW
?
"allow"
:
xpinfo
->
action
==
XFRM_POLICY_BLOCK
?
"block"
:
"unknown-action"
));
fprintf
(
fp
,
"index %u "
,
xpinfo
->
index
);
fprintf
(
fp
,
"priority %u "
,
xpinfo
->
priority
);
fprintf
(
fp
,
"share %s "
,
strxf_share
(
xpinfo
->
share
));
fprintf
(
fp
,
"flags 0x%s"
,
strxf_flags
(
xpinfo
->
flags
));
fprintf
(
fp
,
"
\n
"
);
if
(
show_stats
>
0
)
xfrm_lifetime_print
(
&
xpinfo
->
lft
,
&
xpinfo
->
curlft
,
fp
,
"
\t
"
);
xfrm_xfrma_print
(
tb
,
ntb
,
xpinfo
->
sel
.
family
,
fp
,
"
\t
"
);
return
0
;
}
static
int
xfrm_policy_get_or_delete
(
int
argc
,
char
**
argv
,
int
delete
,
void
*
res_nlbuf
)
{
struct
rtnl_handle
rth
;
struct
{
struct
nlmsghdr
n
;
struct
xfrm_userpolicy_id
xpid
;
}
req
;
char
*
dirp
=
NULL
;
char
*
selp
=
NULL
;
char
*
indexp
=
NULL
;
memset
(
&
req
,
0
,
sizeof
(
req
));
req
.
n
.
nlmsg_len
=
NLMSG_LENGTH
(
sizeof
(
req
.
xpid
));
req
.
n
.
nlmsg_flags
=
NLM_F_REQUEST
;
req
.
n
.
nlmsg_type
=
delete
?
XFRM_MSG_DELPOLICY
:
XFRM_MSG_GETPOLICY
;
while
(
argc
>
0
)
{
if
(
strcmp
(
*
argv
,
"dir"
)
==
0
)
{
if
(
dirp
)
duparg
(
"dir"
,
*
argv
);
dirp
=
*
argv
;
NEXT_ARG
();
xfrm_policy_dir_parse
(
&
req
.
xpid
.
dir
,
&
argc
,
&
argv
);
}
else
if
(
strcmp
(
*
argv
,
"sel"
)
==
0
)
{
if
(
selp
)
duparg
(
"sel"
,
*
argv
);
selp
=
*
argv
;
NEXT_ARG
();
xfrm_selector_parse
(
&
req
.
xpid
.
sel
,
&
argc
,
&
argv
);
if
(
preferred_family
==
AF_UNSPEC
)
preferred_family
=
req
.
xpid
.
sel
.
family
;
}
else
if
(
strcmp
(
*
argv
,
"index"
)
==
0
)
{
if
(
indexp
)
duparg
(
"index"
,
*
argv
);
indexp
=
*
argv
;
NEXT_ARG
();
if
(
get_u32
(
&
req
.
xpid
.
index
,
*
argv
,
0
))
invarg
(
"
\"
INDEX
\"
is invalid"
,
*
argv
);
}
else
invarg
(
"unknown"
,
*
argv
);
argc
--
;
argv
++
;
}
if
(
!
dirp
)
{
fprintf
(
stderr
,
"Not enough information:
\"
DIR
\"
is required.
\n
"
);
exit
(
1
);
}
if
(
!
selp
&&
!
indexp
)
{
fprintf
(
stderr
,
"Not enough information: either
\"
SELECTOR
\"
or
\"
INDEX
\"
is required.
\n
"
);
exit
(
1
);
}
if
(
selp
&&
indexp
)
duparg2
(
"SELECTOR"
,
"INDEX"
);
if
(
rtnl_open_byproto
(
&
rth
,
0
,
NETLINK_XFRM
)
<
0
)
exit
(
1
);
if
(
req
.
xpid
.
sel
.
family
==
AF_UNSPEC
)
req
.
xpid
.
sel
.
family
=
AF_INET
;
if
(
rtnl_talk
(
&
rth
,
&
req
.
n
,
0
,
0
,
res_nlbuf
,
NULL
,
NULL
)
<
0
)
exit
(
2
);
rtnl_close
(
&
rth
);
return
0
;
}
/*
* Smaller scored tmpl should be first for a policy.
* Score=0 means unknown protocol or one which can't be judged.
*/
static
int
xfrm_id_proto_score
(
__u8
proto
,
__u8
dir
)
{
/*
* Basically, score is reversed order as extensions headers
*
* <- large small ->
* [IP][..][RT2][HAO][..][AH][ESP][COMP][..]
*
* Exception:
* At outbound case, any protocols should be followed AH.
*/
switch
(
proto
)
{
case
IPPROTO_ESP
:
return
(
1
<<
3
);
case
IPPROTO_AH
:
if
(
dir
==
XFRM_POLICY_IN
)
return
(
1
<<
4
);
else
return
(
1
<<
12
);
case
IPPROTO_COMP
:
return
(
1
<<
2
);
#ifdef USE_MIP6
case
IPPROTO_ROUTING
:
/* assuming RT2 */
return
(
1
<<
7
);
case
IPPROTO_DSTOPTS
:
/* assuming HAO */
return
(
1
<<
6
);
#endif
default:
break
;
}
return
0
;
}
/*
* Compare templates t1 and t2 and return less than, equal to, or greater
* than zero if t1 is found respectively, to be before, to be the same,
* or to be after than t2 for its "preferred" order for a policy
* in using under IPsec and/or MIPv6.
*/
static
int
xfrm_tmpl_preferred_cmp
(
struct
xfrm_user_tmpl
*
t1
,
struct
xfrm_user_tmpl
*
t2
,
__u8
dir
)
{
int
score1
;
int
score2
;
score1
=
xfrm_id_proto_score
(
t1
->
id
.
proto
,
dir
);
if
(
score1
==
0
)
{
/* assume t1 as lower score */
return
-
1
;
}
score2
=
xfrm_id_proto_score
(
t2
->
id
.
proto
,
dir
);
return
(
score1
-
score2
);
}
static
int
xfrm_tmpl_extract
(
struct
rtattr
*
tb
[],
int
ntb
,
struct
xfrm_user_tmpl
*
tmpls
,
int
*
ntmpls
)
{
int
idx
=
0
;
__u16
type
;
int
n
;
struct
xfrm_user_tmpl
*
t
;
int
i
;
int
j
;
/* store existing templates */
for
(
i
=
0
;
i
<
ntb
;
i
++
)
{
type
=
tb
[
i
]
->
rta_type
;
if
(
type
!=
XFRMA_TMPL
)
{
fprintf
(
stderr
,
"Policy has attr which is not template: %u
\n
"
,
type
);
exit
(
1
);
}
n
=
tb
[
i
]
->
rta_len
/
sizeof
(
struct
xfrm_user_tmpl
);
t
=
(
struct
xfrm_user_tmpl
*
)
RTA_DATA
(
tb
[
i
]);
for
(
j
=
0
;
j
<
n
;
j
++
)
{
memcpy
(
&
tmpls
[
idx
],
&
t
[
j
],
sizeof
(
tmpls
[
idx
]));
idx
++
;
}
}
*
ntmpls
=
idx
;
return
0
;
}
/*
* (existing tmpl=ET, new tmpls=NT)
* Any ET keeps sequence within ETs (ET never changes order within ETs).
* It is also NT's case. With those rules, tmpls are merged by preferred order.
*/
static
int
xfrm_tmpl_merge
(
struct
xfrm_user_tmpl
*
tcur
,
int
n_tcur
,
struct
xfrm_user_tmpl
*
tnew
,
int
n_tnew
,
__u8
dir
,
struct
nlmsghdr
*
n
,
int
len
)
{
struct
tmpl_list
{
struct
tmpl_list
*
next
;
struct
xfrm_user_tmpl
*
tmplp
;
};
struct
tmpl_list
*
head
=
NULL
;
struct
tmpl_list
*
prev
;
struct
tmpl_list
*
p
;
struct
xfrm_user_tmpl
tmpls
[
XFRM_MAX_DEPTH
];
int
ntmpls
;
int
i
;
if
(
n_tcur
+
n_tnew
>
XFRM_MAX_DEPTH
)
{
fprintf
(
stderr
,
"Too many tmpls when merging: exists=%u + new=%u makes overflow
\n
"
,
n_tcur
,
n_tnew
);
exit
(
1
);
}
fprintf
(
stderr
,
"DEBUG: %s: dir = %s
\n
"
,
__FUNCTION__
,
(
dir
==
XFRM_POLICY_IN
?
"in "
:
dir
==
XFRM_POLICY_OUT
?
"out"
:
dir
==
XFRM_POLICY_FWD
?
"fwd"
:
"unknown-dir"
));
/* store existing templates to list */
prev
=
NULL
;
for
(
i
=
0
;
i
<
n_tcur
;
i
++
)
{
struct
tmpl_list
*
tl
;
tl
=
malloc
(
sizeof
(
struct
tmpl_list
));
if
(
!
tl
)
{
perror
(
"malloc"
);
exit
(
1
);
}
tl
->
next
=
NULL
;
tl
->
tmplp
=
&
tcur
[
i
];
fprintf
(
stderr
,
"DEBUG: kernel tmpl %d = %u
\n
"
,
i
+
1
,
tl
->
tmplp
->
id
.
proto
);
if
(
!
prev
)
head
=
tl
;
else
prev
->
next
=
tl
;
prev
=
tl
;
}
#if 1
for
(
i
=
0
;
i
<
n_tnew
;
i
++
)
fprintf
(
stderr
,
"DEBUG: new tmpl %d = %u
\n
"
,
i
+
1
,
tnew
[
i
].
id
.
proto
);
#endif
/* order them; compare and insert new one to list */
i
=
0
;
prev
=
NULL
;
for
(
p
=
head
;
p
;
p
=
p
->
next
)
{
int
cmp
=
xfrm_tmpl_preferred_cmp
(
p
->
tmplp
,
&
tnew
[
i
],
dir
);
if
(
cmp
>
0
)
{
struct
tmpl_list
*
tl
;
tl
=
malloc
(
sizeof
(
struct
tmpl_list
));
if
(
!
tl
)
{
perror
(
"malloc"
);
exit
(
1
);
}
tl
->
tmplp
=
&
tnew
[
i
];
tl
->
next
=
p
;
if
(
p
==
head
)
head
=
tl
;
if
(
prev
)
prev
->
next
=
tl
;
p
=
tl
;
i
++
;
}
prev
=
p
;
if
(
i
>=
n_tnew
)
break
;
}
if
(
!
p
)
{
/* append rest of new tmpls to the end of list */
p
=
prev
;
for
(;
i
<
n_tnew
;
i
++
)
{
struct
tmpl_list
*
tl
;
tl
=
malloc
(
sizeof
(
struct
tmpl_list
));
if
(
!
tl
)
{
perror
(
"malloc"
);
exit
(
1
);
}
tl
->
tmplp
=
&
tnew
[
i
];
tl
->
next
=
NULL
;
if
(
!
p
)
/* no kernel tmpls case */
head
=
tl
;
else
p
->
next
=
tl
;
p
=
tl
;
}
}
/* store ordered list to buffer */
memset
(
&
tmpls
,
0
,
sizeof
(
tmpls
));
i
=
0
;
for
(
p
=
head
;
p
;
p
=
p
->
next
)
{
if
(
i
>=
sizeof
(
tmpls
)
/
sizeof
(
tmpls
[
0
]))
{
fprintf
(
stderr
,
"Too many tmpls: buffer overflow
\n
"
);
exit
(
1
);
}
memcpy
(
&
tmpls
[
i
],
p
->
tmplp
,
sizeof
(
tmpls
[
i
]));
fprintf
(
stderr
,
"DEBUG: merged tmpl %d = %u
\n
"
,
i
+
1
,
p
->
tmplp
->
id
.
proto
);
i
++
;
}
ntmpls
=
i
;
prev
=
NULL
;
for
(
p
=
head
;
p
;
p
=
p
->
next
)
{
if
(
prev
)
free
(
prev
);
prev
=
p
;
}
if
(
prev
)
free
(
prev
);
addattr_l
(
n
,
len
,
XFRMA_TMPL
,
(
void
*
)
tmpls
,
(
sizeof
(
struct
xfrm_user_tmpl
)
*
ntmpls
));
return
0
;
}
/*
* (existing tmpl=ET, new tmpls=NT)
* Updates ET by specified NT at each order without "remain" NT.
*/
static
int
xfrm_tmpl_change
(
struct
xfrm_user_tmpl
*
tcur
,
int
n_tcur
,
struct
xfrm_user_tmpl
*
tnew
,
int
n_tnew
,
__u8
dir
,
struct
nlmsghdr
*
n
,
int
len
)
{
struct
xfrm_user_tmpl
tmpls
[
XFRM_MAX_DEPTH
];
int
ntmpls
;
int
n_changed
=
0
;
int
i
;
if
(
n_tcur
!=
n_tnew
)
{
fprintf
(
stderr
,
"Templates count differs: %d != %d
\n
"
,
n_tcur
,
n_tnew
);
exit
(
1
);
}
if
(
n_tcur
>=
sizeof
(
tmpls
)
/
sizeof
(
tmpls
[
0
]))
{
fprintf
(
stderr
,
"Too many tmpls: buffer overflow
\n
"
);
exit
(
1
);
}
fprintf
(
stderr
,
"DEBUG: %s: dir = %s
\n
"
,
__FUNCTION__
,
(
dir
==
XFRM_POLICY_IN
?
"in "
:
dir
==
XFRM_POLICY_OUT
?
"out"
:
dir
==
XFRM_POLICY_FWD
?
"fwd"
:
"unknown-dir"
));
/* change it to new one except remain request */
for
(
i
=
0
;
i
<
n_tcur
;
i
++
)
{
struct
xfrm_user_tmpl
*
t
;
if
(
tnew
[
i
].
id
.
proto
==
0
)
/* remain case */
t
=
&
tcur
[
i
];
else
{
t
=
&
tnew
[
i
];
n_changed
++
;
}
memcpy
(
&
tmpls
[
i
],
t
,
sizeof
(
tmpls
[
i
]));
}
ntmpls
=
n_tcur
;
if
(
n_changed
==
0
)
{
fprintf
(
stderr
,
"No template to be changed is found.
\n
"
);
exit
(
1
);
}
fprintf
(
stderr
,
"DEBUG: changed tmpls = %d
\n
"
,
n_changed
);
addattr_l
(
n
,
len
,
XFRMA_TMPL
,
(
void
*
)
tmpls
,
(
sizeof
(
struct
xfrm_user_tmpl
)
*
ntmpls
));
return
0
;
}
/*
* (existing tmpl=ET, new tmpls=NT)
* Updates the first ET whose protocol is matched with NT once.
*/
static
int
xfrm_tmpl_replace
(
struct
xfrm_user_tmpl
*
tcur
,
int
n_tcur
,
struct
xfrm_user_tmpl
*
tnew
,
int
n_tnew
,
__u8
dir
,
struct
nlmsghdr
*
n
,
int
len
)
{
struct
xfrm_user_tmpl
tmpls
[
XFRM_MAX_DEPTH
];
int
ntmpls
;
int
n_replaced
=
0
;
int
i
;
int
j
;
if
(
n_tcur
<
n_tnew
)
{
fprintf
(
stderr
,
"Too many tmpls: %d < %d: replace request should be less than current
\n
"
,
n_tcur
,
n_tnew
);
exit
(
1
);
}
if
(
n_tcur
>=
sizeof
(
tmpls
)
/
sizeof
(
tmpls
[
0
]))
{
fprintf
(
stderr
,
"Too many tmpls: buffer overflow
\n
"
);
exit
(
1
);
}
/* check if duplcate protocol is found */
for
(
i
=
0
;
i
<
n_tnew
;
i
++
)
{
for
(
j
=
i
+
1
;
j
<
n_tnew
;
j
++
)
{
if
(
tnew
[
j
].
id
.
proto
==
tnew
[
i
].
id
.
proto
)
{
fprintf
(
stderr
,
"Duplicate protocol specified by replacing request
\n
"
);
exit
(
1
);
}
}
}
/* at first, just store existing tmpls to buffer */
for
(
i
=
0
;
i
<
n_tcur
;
i
++
)
memcpy
(
&
tmpls
[
i
],
&
tcur
[
i
],
sizeof
(
tmpls
[
i
]));
ntmpls
=
n_tcur
;
/* compare and change it to new one */
for
(
i
=
0
;
i
<
n_tnew
;
i
++
)
{
if
(
tnew
[
i
].
id
.
proto
==
0
)
/* remain case */
continue
;
for
(
j
=
0
;
j
<
ntmpls
;
j
++
)
{
if
(
tmpls
[
j
].
id
.
proto
==
tnew
[
i
].
id
.
proto
)
{
memcpy
(
&
tmpls
[
j
],
&
tnew
[
i
],
sizeof
(
tmpls
[
j
]));
n_replaced
++
;
break
;
}
}
}
if
(
n_replaced
==
0
)
{
fprintf
(
stderr
,
"No template to be replaced is found.
\n
"
);
exit
(
1
);
}
fprintf
(
stderr
,
"DEBUG: %s: dir = %s
\n
"
,
__FUNCTION__
,
(
dir
==
XFRM_POLICY_IN
?
"in "
:
dir
==
XFRM_POLICY_OUT
?
"out"
:
dir
==
XFRM_POLICY_FWD
?
"fwd"
:
"unknown-dir"
));
fprintf
(
stderr
,
"DEBUG: replaced tmpls = %d
\n
"
,
n_replaced
);
addattr_l
(
n
,
len
,
XFRMA_TMPL
,
(
void
*
)
tmpls
,
(
sizeof
(
struct
xfrm_user_tmpl
)
*
ntmpls
));
return
0
;
}
/*
* To modify templates, get existing policy and then update it.
*
* "modify":
* 0 : To merge both exsiting template and new one.
* 1 : To change existing template by which new one at each order.
* 2 : To replace existing template whose protocol is matched by new one.
* Each replacing occurs once.
*/
static
int
xfrm_policy_tmpl_modify
(
int
cmd
,
unsigned
flags
,
int
argc
,
char
**
argv
,
int
modify
)
{
struct
rtnl_handle
rth
;
struct
{
struct
nlmsghdr
n
;
struct
xfrm_userpolicy_id
xpid
;
}
req
;
char
*
dirp
=
NULL
;
char
*
selp
=
NULL
;
char
*
indexp
=
NULL
;
int
len
;
struct
rtattr
*
tb
[
XFRMA_MAX
+
1
];
int
ntb
;
char
res_buf
[
NLMSG_BUF_SIZE
];
struct
nlmsghdr
*
res_n
;
struct
xfrm_userpolicy_info
*
res_xpinfo
;
struct
xfrm_user_tmpl
tmpls_cur
[
XFRM_MAX_DEPTH
];
int
ntmpls_cur
=
0
;
struct
xfrm_user_tmpl
tmpls_new
[
XFRM_MAX_DEPTH
];
int
ntmpls_new
=
0
;
struct
{
struct
nlmsghdr
n
;
struct
xfrm_userpolicy_info
xpinfo
;
char
buf
[
RTA_BUF_SIZE
];
}
req_upd
;
memset
(
tmpls_cur
,
0
,
sizeof
(
tmpls_cur
));
memset
(
tmpls_new
,
0
,
sizeof
(
tmpls_new
));
memset
(
&
req
,
0
,
sizeof
(
req
));
req
.
n
.
nlmsg_len
=
NLMSG_LENGTH
(
sizeof
(
req
.
xpid
));
req
.
n
.
nlmsg_flags
=
NLM_F_REQUEST
;
req
.
n
.
nlmsg_type
=
XFRM_MSG_GETPOLICY
;
while
(
argc
>
0
)
{
if
(
strcmp
(
*
argv
,
"dir"
)
==
0
)
{
if
(
dirp
)
duparg
(
"dir"
,
*
argv
);
dirp
=
*
argv
;
NEXT_ARG
();
xfrm_policy_dir_parse
(
&
req
.
xpid
.
dir
,
&
argc
,
&
argv
);
}
else
if
(
strcmp
(
*
argv
,
"sel"
)
==
0
)
{
if
(
selp
)
duparg
(
"sel"
,
*
argv
);
selp
=
*
argv
;
NEXT_ARG
();
xfrm_selector_parse
(
&
req
.
xpid
.
sel
,
&
argc
,
&
argv
);
if
(
preferred_family
==
AF_UNSPEC
)
preferred_family
=
req
.
xpid
.
sel
.
family
;
}
else
if
(
strcmp
(
*
argv
,
"index"
)
==
0
)
{
if
(
indexp
)
duparg
(
"index"
,
*
argv
);
indexp
=
*
argv
;
NEXT_ARG
();
if
(
get_u32
(
&
req
.
xpid
.
index
,
*
argv
,
0
))
invarg
(
"
\"
INDEX
\"
is invalid"
,
*
argv
);
}
else
if
(
strcmp
(
*
argv
,
"tmpl"
)
==
0
)
{
struct
xfrm_user_tmpl
*
tmpl
;
if
(
ntmpls_new
>=
sizeof
(
tmpls_new
)
/
sizeof
(
struct
xfrm_user_tmpl
))
{
fprintf
(
stderr
,
"Too many tmpls: buffer overflow: %d
\n
"
,
ntmpls_new
);
exit
(
1
);
}
tmpl
=
&
tmpls_new
[
ntmpls_new
];
ntmpls_new
++
;
memset
(
tmpl
,
0
,
sizeof
(
*
tmpl
));
tmpl
->
family
=
preferred_family
;
tmpl
->
aalgos
=
(
~
(
__u32
)
0
);
tmpl
->
ealgos
=
(
~
(
__u32
)
0
);
tmpl
->
calgos
=
(
~
(
__u32
)
0
);
NEXT_ARG
();
if
(
strcmp
(
*
argv
,
"remain"
)
==
0
)
{
if
(
modify
==
0
)
invarg
(
"invalid with merge command"
,
*
argv
);
/*
* do nothing;
* id.proto = 0 will be assumed remain one
* after here.
*/
}
else
xfrm_tmpl_parse
(
tmpl
,
&
argc
,
&
argv
);
}
else
invarg
(
"unknown"
,
*
argv
);
argc
--
;
argv
++
;
}
if
(
!
dirp
)
{
fprintf
(
stderr
,
"Not enough information:
\"
DIR
\"
is required.
\n
"
);
exit
(
1
);
}
if
(
!
selp
&&
!
indexp
)
{
fprintf
(
stderr
,
"Not enough information: either
\"
SELECTOR
\"
or
\"
INDEX
\"
is required.
\n
"
);
exit
(
1
);
}
if
(
rtnl_open_byproto
(
&
rth
,
0
,
NETLINK_XFRM
)
<
0
)
exit
(
1
);
if
(
req
.
xpid
.
sel
.
family
==
AF_UNSPEC
)
req
.
xpid
.
sel
.
family
=
AF_INET
;
memset
(
res_buf
,
0
,
sizeof
(
res_buf
));
res_n
=
(
struct
nlmsghdr
*
)
res_buf
;
/* try to get an existing policy */
if
(
rtnl_talk
(
&
rth
,
&
req
.
n
,
0
,
0
,
res_n
,
NULL
,
NULL
)
<
0
)
exit
(
2
);
res_xpinfo
=
(
struct
xfrm_userpolicy_info
*
)
NLMSG_DATA
(
res_n
);
len
=
res_n
->
nlmsg_len
-
NLMSG_LENGTH
(
sizeof
(
*
res_xpinfo
));
if
(
len
<
0
)
{
fprintf
(
stderr
,
"BUG: wrong nlmsg len %d
\n
"
,
len
);
return
-
1
;
}
memset
(
tb
,
0
,
sizeof
(
tb
));
ntb
=
parse_rtattr_byindex
(
tb
,
XFRM_MAX_DEPTH
,
XFRMP_RTA
(
res_xpinfo
),
len
);
ntmpls_cur
=
0
;
xfrm_tmpl_extract
(
tb
,
ntb
,
tmpls_cur
,
&
ntmpls_cur
);
memset
(
&
req_upd
,
0
,
sizeof
(
req_upd
));
req_upd
.
n
.
nlmsg_len
=
NLMSG_LENGTH
(
sizeof
(
req_upd
.
xpinfo
));
req_upd
.
n
.
nlmsg_flags
=
NLM_F_REQUEST
|
flags
;
req_upd
.
n
.
nlmsg_type
=
XFRM_MSG_UPDPOLICY
;
memcpy
(
&
req_upd
.
xpinfo
,
res_xpinfo
,
sizeof
(
req_upd
.
xpinfo
));
switch
(
modify
)
{
case
0
:
/* merging templates */
xfrm_tmpl_merge
(
tmpls_cur
,
ntmpls_cur
,
tmpls_new
,
ntmpls_new
,
req
.
xpid
.
dir
,
&
req_upd
.
n
,
sizeof
(
req_upd
));
break
;
case
1
:
/* changing templates */
xfrm_tmpl_change
(
tmpls_cur
,
ntmpls_cur
,
tmpls_new
,
ntmpls_new
,
req
.
xpid
.
dir
,
&
req_upd
.
n
,
sizeof
(
req_upd
));
break
;
case
2
:
/* replacing templates */
xfrm_tmpl_replace
(
tmpls_cur
,
ntmpls_cur
,
tmpls_new
,
ntmpls_new
,
req
.
xpid
.
dir
,
&
req_upd
.
n
,
sizeof
(
req_upd
));
break
;
default:
/* not reached */
fprintf
(
stderr
,
"Internal error
\n
"
);
exit
(
1
);
}
if
(
req_upd
.
xpinfo
.
sel
.
family
==
AF_UNSPEC
)
req_upd
.
xpinfo
.
sel
.
family
=
AF_INET
;
/* update with new poilcy */
if
(
rtnl_talk
(
&
rth
,
&
req_upd
.
n
,
0
,
0
,
NULL
,
NULL
,
NULL
)
<
0
)
exit
(
2
);
rtnl_close
(
&
rth
);
return
0
;
}
static
int
xfrm_policy_delete
(
int
argc
,
char
**
argv
)
{
return
xfrm_policy_get_or_delete
(
argc
,
argv
,
1
,
NULL
);
}
static
int
xfrm_policy_get
(
int
argc
,
char
**
argv
)
{
char
buf
[
NLMSG_BUF_SIZE
];
struct
nlmsghdr
*
n
=
(
struct
nlmsghdr
*
)
buf
;
memset
(
buf
,
0
,
sizeof
(
buf
));
xfrm_policy_get_or_delete
(
argc
,
argv
,
0
,
n
);
if
(
xfrm_policy_print
(
NULL
,
n
,
(
void
*
)
stdout
)
<
0
)
{
fprintf
(
stderr
,
"An error :-)
\n
"
);
exit
(
1
);
}
return
0
;
}
/*
* With an existing policy of nlmsg, make new nlmsg for deleting the policy
* and store it to buffer.
*/
int
xfrm_policy_keep
(
struct
sockaddr_nl
*
who
,
struct
nlmsghdr
*
n
,
void
*
arg
)
{
struct
xfrm_buffer
*
xb
=
(
struct
xfrm_buffer
*
)
arg
;
struct
rtnl_handle
*
rth
=
xb
->
rth
;
struct
xfrm_userpolicy_info
*
xpinfo
=
NLMSG_DATA
(
n
);
int
len
=
n
->
nlmsg_len
;
struct
nlmsghdr
*
new_n
;
struct
xfrm_userpolicy_id
*
xpid
;
if
(
n
->
nlmsg_type
!=
XFRM_MSG_NEWPOLICY
)
{
fprintf
(
stderr
,
"Not a policy: %08x %08x %08x
\n
"
,
n
->
nlmsg_len
,
n
->
nlmsg_type
,
n
->
nlmsg_flags
);
return
0
;
}
len
-=
NLMSG_LENGTH
(
sizeof
(
*
xpinfo
));
if
(
len
<
0
)
{
fprintf
(
stderr
,
"BUG: wrong nlmsg len %d
\n
"
,
len
);
return
-
1
;
}
if
(
!
xfrm_policy_filter_match
(
xpinfo
))
return
0
;
if
(
xb
->
offset
>
xb
->
size
)
{
fprintf
(
stderr
,
"Flush buffer overflow
\n
"
);
return
-
1
;
}
new_n
=
(
struct
nlmsghdr
*
)(
xb
->
buf
+
xb
->
offset
);
new_n
->
nlmsg_len
=
NLMSG_LENGTH
(
sizeof
(
*
xpid
));
new_n
->
nlmsg_flags
=
NLM_F_REQUEST
;
new_n
->
nlmsg_type
=
XFRM_MSG_DELPOLICY
;
new_n
->
nlmsg_seq
=
++
rth
->
seq
;
xpid
=
NLMSG_DATA
(
new_n
);
memcpy
(
&
xpid
->
sel
,
&
xpinfo
->
sel
,
sizeof
(
xpid
->
sel
));
xpid
->
dir
=
xpinfo
->
dir
;
xpid
->
index
=
xpinfo
->
index
;
xb
->
offset
+=
new_n
->
nlmsg_len
;
xb
->
nlmsg_count
++
;
return
0
;
}
static
int
xfrm_policy_list_or_flush
(
int
argc
,
char
**
argv
,
int
flush
)
{
struct
rtnl_handle
rth
;
if
(
argc
>
0
)
filter
.
use
=
1
;
filter
.
xpinfo
.
sel
.
family
=
preferred_family
;
while
(
argc
>
0
)
{
if
(
strcmp
(
*
argv
,
"dir"
)
==
0
)
{
NEXT_ARG
();
xfrm_policy_dir_parse
(
&
filter
.
xpinfo
.
dir
,
&
argc
,
&
argv
);
filter
.
dir_mask
=
XFRM_FILTER_MASK_FULL
;
}
else
if
(
strcmp
(
*
argv
,
"sel"
)
==
0
)
{
NEXT_ARG
();
xfrm_selector_parse
(
&
filter
.
xpinfo
.
sel
,
&
argc
,
&
argv
);
if
(
preferred_family
==
AF_UNSPEC
)
preferred_family
=
filter
.
xpinfo
.
sel
.
family
;
}
else
if
(
strcmp
(
*
argv
,
"index"
)
==
0
)
{
NEXT_ARG
();
if
(
get_u32
(
&
filter
.
xpinfo
.
index
,
*
argv
,
0
))
invarg
(
"
\"
INDEX
\"
is invalid"
,
*
argv
);
filter
.
index_mask
=
XFRM_FILTER_MASK_FULL
;
}
else
if
(
strcmp
(
*
argv
,
"action"
)
==
0
)
{
NEXT_ARG
();
if
(
strcmp
(
*
argv
,
"allow"
)
==
0
)
filter
.
xpinfo
.
action
=
XFRM_POLICY_ALLOW
;
else
if
(
strcmp
(
*
argv
,
"block"
)
==
0
)
filter
.
xpinfo
.
action
=
XFRM_POLICY_BLOCK
;
else
invarg
(
"
\"
action
\"
value is invalid
\n
"
,
*
argv
);
filter
.
action_mask
=
XFRM_FILTER_MASK_FULL
;
}
else
if
(
strcmp
(
*
argv
,
"priority"
)
==
0
)
{
NEXT_ARG
();
if
(
get_u32
(
&
filter
.
xpinfo
.
priority
,
*
argv
,
0
))
invarg
(
"
\"
PRIORITY
\"
is invalid"
,
*
argv
);
filter
.
priority_mask
=
XFRM_FILTER_MASK_FULL
;
}
else
invarg
(
"unknown"
,
*
argv
);
argc
--
;
argv
++
;
}
if
(
rtnl_open_byproto
(
&
rth
,
0
,
NETLINK_XFRM
)
<
0
)
exit
(
1
);
if
(
flush
)
{
struct
xfrm_buffer
xb
;
char
buf
[
NLMSG_FLUSH_BUF_SIZE
];
int
i
;
xb
.
buf
=
buf
;
xb
.
size
=
sizeof
(
buf
);
xb
.
rth
=
&
rth
;
for
(
i
=
0
;
;
i
++
)
{
xb
.
offset
=
0
;
xb
.
nlmsg_count
=
0
;
if
(
show_stats
>
1
)
fprintf
(
stderr
,
"Flush round = %d
\n
"
,
i
);
if
(
rtnl_wilddump_request
(
&
rth
,
preferred_family
,
XFRM_MSG_GETPOLICY
)
<
0
)
{
perror
(
"Cannot send dump request"
);
exit
(
1
);
}
if
(
rtnl_dump_filter
(
&
rth
,
xfrm_policy_keep
,
&
xb
,
NULL
,
NULL
)
<
0
)
{
fprintf
(
stderr
,
"Flush terminated
\n
"
);
exit
(
1
);
}
if
(
xb
.
nlmsg_count
==
0
)
{
if
(
show_stats
>
1
)
fprintf
(
stderr
,
"Flush completed
\n
"
);
break
;
}
if
(
rtnl_send
(
&
rth
,
xb
.
buf
,
xb
.
offset
)
<
0
)
{
perror
(
"Failed to send flush request
\n
"
);
exit
(
1
);
}
if
(
show_stats
>
1
)
fprintf
(
stderr
,
"Flushed nlmsg count = %d
\n
"
,
xb
.
nlmsg_count
);
xb
.
offset
=
0
;
xb
.
nlmsg_count
=
0
;
}
}
else
{
if
(
rtnl_wilddump_request
(
&
rth
,
preferred_family
,
XFRM_MSG_GETPOLICY
)
<
0
)
{
perror
(
"Cannot send dump request"
);
exit
(
1
);
}
if
(
rtnl_dump_filter
(
&
rth
,
xfrm_policy_print
,
stdout
,
NULL
,
NULL
)
<
0
)
{
fprintf
(
stderr
,
"Dump terminated
\n
"
);
exit
(
1
);
}
}
rtnl_close
(
&
rth
);
exit
(
0
);
}
int
do_xfrm_policy
(
int
argc
,
char
**
argv
)
{
if
(
argc
<
1
)
return
xfrm_policy_list_or_flush
(
0
,
NULL
,
0
);
#if 0
/*
* NLM_F_X is not supported for xfrm in the kernel.
*/
if (matches(*argv, "add") == 0)
return xfrm_policy_modify(XFRM_MSG_NEWPOLICY, NLM_F_CREATE|NLM_F_EXCL,
argc-1, argv+1);
if (matches(*argv, "change") == 0 || strcmp(*argv, "chg") == 0)
return xfrm_policy_modify(XFRM_MSG_NEWPOLICY, NLM_F_REPLACE,
argc-1, argv+1);
if (matches(*argv, "replace") == 0)
return xfrm_policy_modify(XFRM_MSG_NEWPOLICY, NLM_F_CREATE|NLM_F_REPLACE,
argc-1, argv+1);
if (matches(*argv, "prepend") == 0)
return xfrm_policy_modify(XFRM_MSG_NEWPOLICY, NLM_F_CREATE,
argc-1, argv+1);
if (matches(*argv, "append") == 0)
return xfrm_policy_modify(XFRM_MSG_NEWPOLICY, NLM_F_CREATE|NLM_F_APPEND,
argc-1, argv+1);
if (matches(*argv, "test") == 0)
return xfrm_policy_modify(XFRM_MSG_NEWPOLICY, NLM_F_EXCL,
argc-1, argv+1);
#endif
if
(
matches
(
*
argv
,
"add"
)
==
0
)
return
xfrm_policy_modify
(
XFRM_MSG_NEWPOLICY
,
0
,
argc
-
1
,
argv
+
1
);
if
(
matches
(
*
argv
,
"update"
)
==
0
)
return
xfrm_policy_modify
(
XFRM_MSG_UPDPOLICY
,
0
,
argc
-
1
,
argv
+
1
);
if
(
matches
(
*
argv
,
"merge"
)
==
0
)
return
xfrm_policy_tmpl_modify
(
XFRM_MSG_UPDPOLICY
,
0
,
argc
-
1
,
argv
+
1
,
0
);
if
(
matches
(
*
argv
,
"change"
)
==
0
)
return
xfrm_policy_tmpl_modify
(
XFRM_MSG_UPDPOLICY
,
0
,
argc
-
1
,
argv
+
1
,
1
);
if
(
matches
(
*
argv
,
"replace"
)
==
0
)
return
xfrm_policy_tmpl_modify
(
XFRM_MSG_UPDPOLICY
,
0
,
argc
-
1
,
argv
+
1
,
2
);
if
(
matches
(
*
argv
,
"delete"
)
==
0
||
matches
(
*
argv
,
"del"
)
==
0
)
return
xfrm_policy_delete
(
argc
-
1
,
argv
+
1
);
if
(
matches
(
*
argv
,
"list"
)
==
0
||
matches
(
*
argv
,
"show"
)
==
0
||
matches
(
*
argv
,
"lst"
)
==
0
)
return
xfrm_policy_list_or_flush
(
argc
-
1
,
argv
+
1
,
0
);
if
(
matches
(
*
argv
,
"get"
)
==
0
)
return
xfrm_policy_get
(
argc
-
1
,
argv
+
1
);
if
(
matches
(
*
argv
,
"flush"
)
==
0
)
return
xfrm_policy_list_or_flush
(
argc
-
1
,
argv
+
1
,
1
);
if
(
matches
(
*
argv
,
"help"
)
==
0
)
usage
();
fprintf
(
stderr
,
"Command
\"
%s
\"
is unknown, try
\"
ip xfrm policy help
\"
.
\n
"
,
*
argv
);
exit
(
-
1
);
}
iproute2/ip/xfrm_state.c
View file @
9111bd8e
/* $USAGI: $ */
/*
* Copyright (C)2004 USAGI/WIDE Project
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* based on iproute.c
*/
/*
* Authors:
* Masahide NAKAMURA @USAGI
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <linux/xfrm.h>
#include "utils.h"
#include "xfrm.h"
#include "ip_common.h"
//#define NLMSG_FLUSH_BUF_SIZE (4096-512)
#define NLMSG_FLUSH_BUF_SIZE 8192
/*
* Receiving buffer defines:
* nlmsg
* data = struct xfrm_usersa_info
* rtattr
* rtattr
* ... (max count of rtattr is XFRM_MAX_DEPTH)
*
* each rtattr data = struct xfrm_algo(dynamic size) or xfrm_address_t
*/
#define NLMSG_BUF_SIZE 4096
#define RTA_BUF_SIZE 2048
#define XFRM_ALGO_KEY_BUF_SIZE 512
static
void
usage
(
void
)
__attribute__
((
noreturn
));
static
void
usage
(
void
)
{
#ifdef USE_MIP6
fprintf
(
stderr
,
"Usage: ip xfrm state { add | update } ID [ XFRM_OPT ] [ mode MODE ]
\n
"
);
#else
fprintf
(
stderr
,
"Usage: ip xfrm state { add | update } ID [ ALGO-LIST ] [ mode MODE ]
\n
"
);
#endif
fprintf
(
stderr
,
" [ reqid REQID ] [ FLAG-LIST ] [ sel SELECTOR ] [ LIMIT-LIST ]
\n
"
);
fprintf
(
stderr
,
"Usage: ip xfrm state { delete | get } ID
\n
"
);
fprintf
(
stderr
,
"Usage: ip xfrm state { flush | list } [ ID ] [ mode MODE ] [ reqid REQID ]
\n
"
);
fprintf
(
stderr
,
" [ FLAG_LIST ]
\n
"
);
fprintf
(
stderr
,
"ID := [ src ADDR ] [ dst ADDR ] [ proto XFRM_PROTO ] [ spi SPI ]
\n
"
);
#ifdef USE_MIP6
fprintf
(
stderr
,
"XFRM_PROTO := [ esp | ah | ipcomp | route2 | hao ]
\n
"
);
#else
fprintf
(
stderr
,
"XFRM_PROTO := [ esp | ah | ipcomp ]
\n
"
);
#endif
//fprintf(stderr, "SPI - security parameter index(default=0)(PROTO=esp,ah,ipcomp)\n");
#ifdef USE_MIP6
fprintf
(
stderr
,
"XFRM_OPT := [ ALGO-LIST ] | [ coa ADDR ]
\n
"
);
#endif
fprintf
(
stderr
,
"MODE := [ transport | tunnel ](default=transport)
\n
"
);
//fprintf(stderr, "REQID - number(default=0)\n");
fprintf
(
stderr
,
"FLAG-LIST := [ FLAG-LIST ] [ flag FLAG ]
\n
"
);
fprintf
(
stderr
,
"FLAG := [ noecn | wildrecv ]
\n
"
);
fprintf
(
stderr
,
"ALGO-LIST := [ ALGO-LIST ] | [ algo ALGO ]
\n
"
);
fprintf
(
stderr
,
"ALGO := ALGO_TYPE ALGO_NAME ALGO_KEY
\n
"
);
fprintf
(
stderr
,
"ALGO_TYPE := [ E | A | C ]
\n
"
);
//fprintf(stderr, "ALGO_NAME - algorithm name\n");
//fprintf(stderr, "ALGO_KEY - algorithm key\n");
fprintf
(
stderr
,
"SELECTOR := src ADDR[/PLEN] dst ADDR[/PLEN] [ upspec UPSPEC ] [ dev DEV ]
\n
"
);
fprintf
(
stderr
,
"UPSPEC := proto PROTO [ UPSPEC_OPT ]
\n
"
);
fprintf
(
stderr
,
"UPSPEC_OPT := [ [ sport PORT ] [ dport PORT ] ] |
\n
"
);
#ifdef USE_MIP6
fprintf
(
stderr
,
" [ type TYPE [ code CODE ] ](for PROTO=ipv6-icmp) |
\n
"
);
fprintf
(
stderr
,
" [ type TYPE ](for PROTO=ipv6-mh)
\n
"
);
#else
fprintf
(
stderr
,
" [ type TYPE [ code CODE ] ](for PROTO=ipv6-icmp)
\n
"
);
#endif
//fprintf(stderr, "DEV - device name(default=none)\n");
fprintf
(
stderr
,
"LIMIT-LIST := [ LIMIT-LIST ] | [ limit LIMIT ]
\n
"
);
fprintf
(
stderr
,
"LIMIT := [ [time-soft|time-hard|time-use-soft|time-use-hard] SECONDS ] |
\n
"
);
fprintf
(
stderr
,
" [ [byte-soft|byte-hard] SIZE ] | [ [packet-soft|packet-hard] COUNT ]
\n
"
);
exit
(
-
1
);
}
static
int
xfrm_algo_parse
(
struct
xfrm_algo
*
alg
,
enum
xfrm_attr_type_t
type
,
char
*
name
,
char
*
key
,
int
max
)
{
int
len
;
#if 1
/* XXX: verifying both name and key is required! */
fprintf
(
stderr
,
"warning: ALGONAME/ALGOKEY will send to kernel promiscuously!(verifying them isn't implemented yet)
\n
"
);
#endif
strncpy
(
alg
->
alg_name
,
name
,
sizeof
(
alg
->
alg_name
));
if
(
strncmp
(
key
,
"0x"
,
2
)
==
0
)
{
/*
* XXX: fix me!!
*/
__u64
val
=
0
;
char
*
p
=
(
char
*
)
&
val
;
if
(
get_u64
(
&
val
,
key
,
16
))
invarg
(
"
\"
ALGOKEY
\"
is invalid"
,
key
);
len
=
(
strlen
(
key
)
-
2
)
/
2
;
if
(
len
>
sizeof
(
val
))
invarg
(
"
\"
ALGOKEY
\"
is invalid: too large"
,
key
);
if
(
len
>
0
)
{
int
index
=
sizeof
(
val
)
-
len
;
if
(
len
>
max
)
invarg
(
"
\"
ALGOKEY
\"
makes buffer overflow
\n
"
,
key
);
memcpy
(
alg
->
alg_key
,
&
p
[
index
],
len
);
}
}
else
{
len
=
strlen
(
key
);
if
(
len
>
0
)
{
if
(
len
>
max
)
invarg
(
"
\"
ALGOKEY
\"
makes buffer overflow
\n
"
,
key
);
strncpy
(
alg
->
alg_key
,
key
,
len
);
}
}
alg
->
alg_key_len
=
len
*
8
;
return
0
;
}
static
int
xfrm_state_flag_parse
(
__u8
*
flags
,
int
*
argcp
,
char
***
argvp
)
{
int
argc
=
*
argcp
;
char
**
argv
=
*
argvp
;
if
(
strcmp
(
*
argv
,
"noecn"
)
==
0
)
*
flags
|=
XFRM_STATE_NOECN
;
#ifdef USE_MIP6
else
if
(
strcmp
(
*
argv
,
"wildrecv"
)
==
0
)
*
flags
|=
XFRM_STATE_WILDRECV
;
#endif
else
invarg
(
"
\"
FLAG
\"
is invalid"
,
*
argv
);
filter
.
state_flags_mask
=
XFRM_FILTER_MASK_FULL
;
*
argcp
=
argc
;
*
argvp
=
argv
;
return
0
;
}
static
int
xfrm_state_modify
(
int
cmd
,
unsigned
flags
,
int
argc
,
char
**
argv
)
{
struct
rtnl_handle
rth
;
struct
{
struct
nlmsghdr
n
;
struct
xfrm_usersa_info
xsinfo
;
char
buf
[
RTA_BUF_SIZE
];
}
req
;
char
*
idp
=
NULL
;
char
*
ealgop
=
NULL
;
char
*
aalgop
=
NULL
;
char
*
calgop
=
NULL
;
#ifdef USE_MIP6
char
*
coap
=
NULL
;
#endif
memset
(
&
req
,
0
,
sizeof
(
req
));
req
.
n
.
nlmsg_len
=
NLMSG_LENGTH
(
sizeof
(
req
.
xsinfo
));
req
.
n
.
nlmsg_flags
=
NLM_F_REQUEST
|
flags
;
req
.
n
.
nlmsg_type
=
cmd
;
req
.
xsinfo
.
family
=
preferred_family
;
req
.
xsinfo
.
lft
.
soft_byte_limit
=
XFRM_INF
;
req
.
xsinfo
.
lft
.
hard_byte_limit
=
XFRM_INF
;
req
.
xsinfo
.
lft
.
soft_packet_limit
=
XFRM_INF
;
req
.
xsinfo
.
lft
.
hard_packet_limit
=
XFRM_INF
;
while
(
argc
>
0
)
{
if
(
strcmp
(
*
argv
,
"algo"
)
==
0
)
{
struct
{
struct
xfrm_algo
alg
;
char
buf
[
XFRM_ALGO_KEY_BUF_SIZE
];
}
alg
;
int
len
;
enum
xfrm_attr_type_t
type
;
char
*
name
;
char
*
key
;
NEXT_ARG
();
if
(
strcmp
(
*
argv
,
"E"
)
==
0
)
{
if
(
ealgop
)
duparg
(
"ALGOTYPE"
,
*
argv
);
ealgop
=
*
argv
;
type
=
XFRMA_ALG_CRYPT
;
}
else
if
(
strcmp
(
*
argv
,
"A"
)
==
0
)
{
if
(
aalgop
)
duparg
(
"ALGOTYPE"
,
*
argv
);
aalgop
=
*
argv
;
type
=
XFRMA_ALG_AUTH
;
}
else
if
(
strcmp
(
*
argv
,
"C"
)
==
0
)
{
if
(
calgop
)
duparg
(
"ALGOTYPE"
,
*
argv
);
calgop
=
*
argv
;
type
=
XFRMA_ALG_COMP
;
}
else
invarg
(
"
\"
ALGOTYPE
\"
is invalid
\n
"
,
*
argv
);
if
(
!
NEXT_ARG_OK
())
missarg
(
"ALGONAME"
);
NEXT_ARG
();
name
=
*
argv
;
if
(
!
NEXT_ARG_OK
())
missarg
(
"ALGOKEY"
);
NEXT_ARG
();
key
=
*
argv
;
memset
(
&
alg
,
0
,
sizeof
(
alg
));
xfrm_algo_parse
((
void
*
)
&
alg
,
type
,
name
,
key
,
sizeof
(
alg
.
buf
));
len
=
sizeof
(
struct
xfrm_algo
)
+
alg
.
alg
.
alg_key_len
;
addattr_l
(
&
req
.
n
,
sizeof
(
req
.
buf
),
type
,
(
void
*
)
&
alg
,
len
);
#ifdef USE_MIP6
}
else
if
(
strcmp
(
*
argv
,
"coa"
)
==
0
)
{
inet_prefix
coa
;
if
(
coap
)
duparg
(
"coa"
,
*
argv
);
coap
=
*
argv
;
NEXT_ARG
();
get_prefix
(
&
coa
,
*
argv
,
req
.
xsinfo
.
family
);
if
(
req
.
xsinfo
.
family
==
AF_UNSPEC
)
req
.
xsinfo
.
family
=
coa
.
family
;
if
(
coa
.
bytelen
)
addattr_l
(
&
req
.
n
,
sizeof
(
req
.
buf
),
XFRMA_ADDR
,
&
coa
.
data
,
coa
.
bytelen
);
#endif
}
else
if
(
strcmp
(
*
argv
,
"mode"
)
==
0
)
{
NEXT_ARG
();
xfrm_mode_parse
(
&
req
.
xsinfo
.
mode
,
&
argc
,
&
argv
);
}
else
if
(
strcmp
(
*
argv
,
"reqid"
)
==
0
)
{
NEXT_ARG
();
xfrm_reqid_parse
(
&
req
.
xsinfo
.
reqid
,
&
argc
,
&
argv
);
}
else
if
(
strcmp
(
*
argv
,
"flag"
)
==
0
)
{
NEXT_ARG
();
xfrm_state_flag_parse
(
&
req
.
xsinfo
.
flags
,
&
argc
,
&
argv
);
}
else
if
(
strcmp
(
*
argv
,
"sel"
)
==
0
)
{
NEXT_ARG
();
xfrm_selector_parse
(
&
req
.
xsinfo
.
sel
,
&
argc
,
&
argv
);
}
else
if
(
strcmp
(
*
argv
,
"limit"
)
==
0
)
{
NEXT_ARG
();
xfrm_lifetime_cfg_parse
(
&
req
.
xsinfo
.
lft
,
&
argc
,
&
argv
);
}
else
{
if
(
idp
)
invarg
(
"unknown"
,
*
argv
);
idp
=
*
argv
;
/* ID */
xfrm_id_parse
(
&
req
.
xsinfo
.
saddr
,
&
req
.
xsinfo
.
id
,
&
req
.
xsinfo
.
family
,
&
argc
,
&
argv
);
if
(
preferred_family
==
AF_UNSPEC
)
preferred_family
=
req
.
xsinfo
.
family
;
}
argc
--
;
argv
++
;
}
if
(
!
idp
)
{
fprintf
(
stderr
,
"Not enough information:
\"
ID
\"
is required
\n
"
);
exit
(
1
);
}
if
(
ealgop
||
aalgop
||
calgop
)
{
if
(
req
.
xsinfo
.
id
.
proto
!=
IPPROTO_ESP
&&
req
.
xsinfo
.
id
.
proto
!=
IPPROTO_AH
&&
req
.
xsinfo
.
id
.
proto
!=
IPPROTO_COMP
)
{
fprintf
(
stderr
,
"
\"
ALGO
\"
is invalid with proto=%d
\n
"
,
req
.
xsinfo
.
id
.
proto
);
exit
(
1
);
}
}
else
{
if
(
req
.
xsinfo
.
id
.
proto
==
IPPROTO_ESP
||
req
.
xsinfo
.
id
.
proto
==
IPPROTO_AH
||
req
.
xsinfo
.
id
.
proto
==
IPPROTO_COMP
)
{
fprintf
(
stderr
,
"
\"
ALGO
\"
is required with proto=%d
\n
"
,
req
.
xsinfo
.
id
.
proto
);
exit
(
1
);
}
}
#ifdef USE_MIP6
if
(
coap
)
{
if
(
req
.
xsinfo
.
id
.
proto
!=
IPPROTO_ROUTING
&&
req
.
xsinfo
.
id
.
proto
!=
IPPROTO_DSTOPTS
)
{
fprintf
(
stderr
,
"
\"
COA
\"
is invalid with proto=%d
\n
"
,
req
.
xsinfo
.
id
.
proto
);
exit
(
1
);
}
}
else
{
if
(
req
.
xsinfo
.
id
.
proto
==
IPPROTO_ROUTING
||
req
.
xsinfo
.
id
.
proto
==
IPPROTO_DSTOPTS
)
{
fprintf
(
stderr
,
"
\"
COA
\"
is required with proto=%d
\n
"
,
req
.
xsinfo
.
id
.
proto
);
exit
(
1
);
}
}
#endif
if
(
req
.
xsinfo
.
id
.
spi
)
{
if
(
req
.
xsinfo
.
id
.
proto
==
IPPROTO_ROUTING
||
req
.
xsinfo
.
id
.
proto
==
IPPROTO_DSTOPTS
)
{
fprintf
(
stderr
,
"Invalid spi: %u (zero is requried) with proto=%d
\n
"
,
ntohl
(
req
.
xsinfo
.
id
.
spi
),
req
.
xsinfo
.
id
.
proto
);
exit
(
1
);
}
}
if
(
req
.
xsinfo
.
mode
)
{
if
(
req
.
xsinfo
.
id
.
proto
==
IPPROTO_ROUTING
||
req
.
xsinfo
.
id
.
proto
==
IPPROTO_DSTOPTS
)
{
fprintf
(
stderr
,
"Invalid mode: (transport is requried) with proto=%d
\n
"
,
req
.
xsinfo
.
id
.
proto
);
exit
(
1
);
}
}
if
(
rtnl_open_byproto
(
&
rth
,
0
,
NETLINK_XFRM
)
<
0
)
exit
(
1
);
if
(
req
.
xsinfo
.
family
==
AF_UNSPEC
)
req
.
xsinfo
.
family
=
AF_INET
;
if
(
rtnl_talk
(
&
rth
,
&
req
.
n
,
0
,
0
,
NULL
,
NULL
,
NULL
)
<
0
)
exit
(
2
);
rtnl_close
(
&
rth
);
return
0
;
}
static
int
xfrm_state_filter_match
(
struct
xfrm_usersa_info
*
xsinfo
)
{
if
(
!
filter
.
use
)
return
1
;
if
(
filter
.
id_src_mask
)
if
(
memcmp
(
&
xsinfo
->
saddr
,
&
filter
.
xsinfo
.
saddr
,
filter
.
id_src_mask
)
!=
0
)
return
0
;
if
(
filter
.
id_dst_mask
)
if
(
memcmp
(
&
xsinfo
->
id
.
daddr
,
&
filter
.
xsinfo
.
id
.
daddr
,
filter
.
id_dst_mask
)
!=
0
)
return
0
;
if
((
xsinfo
->
id
.
proto
^
filter
.
xsinfo
.
id
.
proto
)
&
filter
.
id_proto_mask
)
return
0
;
if
((
xsinfo
->
id
.
spi
^
filter
.
xsinfo
.
id
.
spi
)
&
filter
.
id_spi_mask
)
return
0
;
if
((
xsinfo
->
mode
^
filter
.
xsinfo
.
mode
)
&
filter
.
mode_mask
)
return
0
;
if
((
xsinfo
->
reqid
^
filter
.
xsinfo
.
reqid
)
&
filter
.
reqid_mask
)
return
0
;
if
(
filter
.
state_flags_mask
)
if
((
xsinfo
->
flags
&
filter
.
xsinfo
.
flags
)
==
0
)
return
0
;
return
1
;
}
int
xfrm_state_print
(
struct
sockaddr_nl
*
who
,
struct
nlmsghdr
*
n
,
void
*
arg
)
{
FILE
*
fp
=
(
FILE
*
)
arg
;
struct
xfrm_usersa_info
*
xsinfo
=
NLMSG_DATA
(
n
);
int
len
=
n
->
nlmsg_len
;
struct
rtattr
*
tb
[
XFRMA_MAX
+
1
];
int
ntb
;
if
(
n
->
nlmsg_type
!=
XFRM_MSG_NEWSA
&&
n
->
nlmsg_type
!=
XFRM_MSG_DELSA
)
{
fprintf
(
stderr
,
"Not a state: %08x %08x %08x
\n
"
,
n
->
nlmsg_len
,
n
->
nlmsg_type
,
n
->
nlmsg_flags
);
return
0
;
}
len
-=
NLMSG_LENGTH
(
sizeof
(
*
xsinfo
));
if
(
len
<
0
)
{
fprintf
(
stderr
,
"BUG: wrong nlmsg len %d
\n
"
,
len
);
return
-
1
;
}
if
(
!
xfrm_state_filter_match
(
xsinfo
))
return
0
;
memset
(
tb
,
0
,
sizeof
(
tb
));
ntb
=
parse_rtattr_byindex
(
tb
,
XFRM_MAX_DEPTH
,
XFRMS_RTA
(
xsinfo
),
len
);
if
(
n
->
nlmsg_type
==
XFRM_MSG_DELSA
)
fprintf
(
fp
,
"Deleted "
);
xfrm_id_info_print
(
&
xsinfo
->
saddr
,
&
xsinfo
->
id
,
xsinfo
->
mode
,
xsinfo
->
reqid
,
xsinfo
->
family
,
fp
,
NULL
);
if
(
show_stats
>
0
)
{
fprintf
(
fp
,
"
\t
"
);
fprintf
(
fp
,
"seq 0x%08u "
,
xsinfo
->
seq
);
fprintf
(
fp
,
"replay-window %d "
,
xsinfo
->
replay_window
);
fprintf
(
fp
,
"flags "
);
if
(
xsinfo
->
flags
&
XFRM_STATE_NOECN
)
fprintf
(
fp
,
"noecn "
);
#ifdef USE_MIP6
if
(
xsinfo
->
flags
&
XFRM_STATE_WILDRECV
)
fprintf
(
fp
,
"wildrecv "
);
#endif
fprintf
(
fp
,
"(0x%s)"
,
strxf_flags
(
xsinfo
->
flags
));
fprintf
(
fp
,
"
\n
"
);
}
xfrm_xfrma_print
(
tb
,
ntb
,
xsinfo
->
family
,
fp
,
"
\t
"
);
if
(
show_stats
>
0
)
{
fprintf
(
fp
,
"
\t
sel:
\n
"
);
xfrm_selector_print
(
&
xsinfo
->
sel
,
xsinfo
->
family
,
fp
,
"
\t
"
);
}
if
(
show_stats
>
0
)
{
xfrm_lifetime_print
(
&
xsinfo
->
lft
,
&
xsinfo
->
curlft
,
fp
,
"
\t
"
);
xfrm_stats_print
(
&
xsinfo
->
stats
,
fp
,
"
\t
"
);
}
return
0
;
}
static
int
xfrm_state_get_or_delete
(
int
argc
,
char
**
argv
,
int
delete
)
{
struct
rtnl_handle
rth
;
struct
{
struct
nlmsghdr
n
;
struct
xfrm_usersa_id
xsid
;
}
req
;
struct
xfrm_id
id
;
char
*
idp
=
NULL
;
memset
(
&
req
,
0
,
sizeof
(
req
));
req
.
n
.
nlmsg_len
=
NLMSG_LENGTH
(
sizeof
(
req
.
xsid
));
req
.
n
.
nlmsg_flags
=
NLM_F_REQUEST
;
req
.
n
.
nlmsg_type
=
delete
?
XFRM_MSG_DELSA
:
XFRM_MSG_GETSA
;
req
.
xsid
.
family
=
preferred_family
;
while
(
argc
>
0
)
{
#ifndef USE_MIP6
/*
* XXX: Source address is not used and ignore it to follow
* XXX: a manner of setkey e.g. in the case of deleting/getting
* XXX: message of IPsec SA.
*/
xfrm_address_t
ignore_saddr
;
#endif
if
(
idp
)
invarg
(
"unknown"
,
*
argv
);
idp
=
*
argv
;
/* ID */
memset
(
&
id
,
0
,
sizeof
(
id
));
#ifdef USE_MIP6
xfrm_id_parse
(
&
req
.
xsid
.
saddr
,
&
id
,
&
req
.
xsid
.
family
,
&
argc
,
&
argv
);
#else
xfrm_id_parse
(
&
ignore_saddr
,
&
id
,
&
req
.
xsid
.
family
,
&
argc
,
&
argv
);
#endif
memcpy
(
&
req
.
xsid
.
daddr
,
&
id
.
daddr
,
sizeof
(
req
.
xsid
.
daddr
));
req
.
xsid
.
spi
=
id
.
spi
;
req
.
xsid
.
proto
=
id
.
proto
;
argc
--
;
argv
++
;
}
switch
(
req
.
xsid
.
proto
)
{
#ifdef USE_MIP6
case
IPPROTO_ROUTING
:
case
IPPROTO_DSTOPTS
:
if
(
req
.
xsid
.
spi
)
{
fprintf
(
stderr
,
"Invalid spi: %u (zero is requried) with proto=%d
\n
"
,
req
.
xsid
.
spi
,
req
.
xsid
.
proto
);
exit
(
1
);
}
break
;
#endif
default:
break
;
}
if
(
rtnl_open_byproto
(
&
rth
,
0
,
NETLINK_XFRM
)
<
0
)
exit
(
1
);
if
(
req
.
xsid
.
family
==
AF_UNSPEC
)
req
.
xsid
.
family
=
AF_INET
;
if
(
delete
)
{
if
(
rtnl_talk
(
&
rth
,
&
req
.
n
,
0
,
0
,
NULL
,
NULL
,
NULL
)
<
0
)
exit
(
2
);
}
else
{
char
buf
[
NLMSG_BUF_SIZE
];
struct
nlmsghdr
*
res_n
=
(
struct
nlmsghdr
*
)
buf
;
memset
(
buf
,
0
,
sizeof
(
buf
));
if
(
rtnl_talk
(
&
rth
,
&
req
.
n
,
0
,
0
,
res_n
,
NULL
,
NULL
)
<
0
)
exit
(
2
);
if
(
xfrm_state_print
(
NULL
,
res_n
,
(
void
*
)
stdout
)
<
0
)
{
fprintf
(
stderr
,
"An error :-)
\n
"
);
exit
(
1
);
}
}
rtnl_close
(
&
rth
);
return
0
;
}
/*
* With an existing state of nlmsg, make new nlmsg for deleting the state
* and store it to buffer.
*/
int
xfrm_state_keep
(
struct
sockaddr_nl
*
who
,
struct
nlmsghdr
*
n
,
void
*
arg
)
{
struct
xfrm_buffer
*
xb
=
(
struct
xfrm_buffer
*
)
arg
;
struct
rtnl_handle
*
rth
=
xb
->
rth
;
struct
xfrm_usersa_info
*
xsinfo
=
NLMSG_DATA
(
n
);
int
len
=
n
->
nlmsg_len
;
struct
nlmsghdr
*
new_n
;
struct
xfrm_usersa_id
*
xsid
;
if
(
n
->
nlmsg_type
!=
XFRM_MSG_NEWSA
)
{
fprintf
(
stderr
,
"Not a state: %08x %08x %08x
\n
"
,
n
->
nlmsg_len
,
n
->
nlmsg_type
,
n
->
nlmsg_flags
);
return
0
;
}
len
-=
NLMSG_LENGTH
(
sizeof
(
*
xsinfo
));
if
(
len
<
0
)
{
fprintf
(
stderr
,
"BUG: wrong nlmsg len %d
\n
"
,
len
);
return
-
1
;
}
if
(
!
xfrm_state_filter_match
(
xsinfo
))
return
0
;
if
(
xb
->
offset
>
xb
->
size
)
{
fprintf
(
stderr
,
"Flush buffer overflow
\n
"
);
return
-
1
;
}
new_n
=
(
struct
nlmsghdr
*
)(
xb
->
buf
+
xb
->
offset
);
new_n
->
nlmsg_len
=
NLMSG_LENGTH
(
sizeof
(
*
xsid
));
new_n
->
nlmsg_flags
=
NLM_F_REQUEST
;
new_n
->
nlmsg_type
=
XFRM_MSG_DELSA
;
new_n
->
nlmsg_seq
=
++
rth
->
seq
;
xsid
=
NLMSG_DATA
(
new_n
);
xsid
->
family
=
xsinfo
->
family
;
memcpy
(
&
xsid
->
daddr
,
&
xsinfo
->
id
.
daddr
,
sizeof
(
xsid
->
daddr
));
#ifdef USE_MIP6
memcpy
(
&
xsid
->
saddr
,
&
xsinfo
->
saddr
,
sizeof
(
xsid
->
saddr
));
#endif
xsid
->
spi
=
xsinfo
->
id
.
spi
;
xsid
->
proto
=
xsinfo
->
id
.
proto
;
xb
->
offset
+=
new_n
->
nlmsg_len
;
xb
->
nlmsg_count
++
;
return
0
;
}
static
int
xfrm_state_list_or_flush
(
int
argc
,
char
**
argv
,
int
flush
)
{
char
*
idp
=
NULL
;
struct
rtnl_handle
rth
;
filter
.
use
=
1
;
filter
.
xsinfo
.
family
=
preferred_family
;
while
(
argc
>
0
)
{
if
(
strcmp
(
*
argv
,
"mode"
)
==
0
)
{
NEXT_ARG
();
xfrm_mode_parse
(
&
filter
.
xsinfo
.
mode
,
&
argc
,
&
argv
);
filter
.
mode_mask
=
XFRM_FILTER_MASK_FULL
;
}
else
if
(
strcmp
(
*
argv
,
"reqid"
)
==
0
)
{
NEXT_ARG
();
xfrm_reqid_parse
(
&
filter
.
xsinfo
.
reqid
,
&
argc
,
&
argv
);
filter
.
reqid_mask
=
XFRM_FILTER_MASK_FULL
;
}
else
if
(
strcmp
(
*
argv
,
"flag"
)
==
0
)
{
NEXT_ARG
();
xfrm_state_flag_parse
(
&
filter
.
xsinfo
.
flags
,
&
argc
,
&
argv
);
filter
.
state_flags_mask
=
XFRM_FILTER_MASK_FULL
;
}
else
{
if
(
idp
)
invarg
(
"unknown"
,
*
argv
);
idp
=
*
argv
;
/* ID */
xfrm_id_parse
(
&
filter
.
xsinfo
.
saddr
,
&
filter
.
xsinfo
.
id
,
&
filter
.
xsinfo
.
family
,
&
argc
,
&
argv
);
if
(
preferred_family
==
AF_UNSPEC
)
preferred_family
=
filter
.
xsinfo
.
family
;
}
argc
--
;
argv
++
;
}
if
(
rtnl_open_byproto
(
&
rth
,
0
,
NETLINK_XFRM
)
<
0
)
exit
(
1
);
if
(
flush
)
{
struct
xfrm_buffer
xb
;
char
buf
[
NLMSG_FLUSH_BUF_SIZE
];
int
i
;
xb
.
buf
=
buf
;
xb
.
size
=
sizeof
(
buf
);
xb
.
rth
=
&
rth
;
for
(
i
=
0
;
;
i
++
)
{
xb
.
offset
=
0
;
xb
.
nlmsg_count
=
0
;
if
(
show_stats
>
1
)
fprintf
(
stderr
,
"Flush round = %d
\n
"
,
i
);
if
(
rtnl_wilddump_request
(
&
rth
,
preferred_family
,
XFRM_MSG_GETSA
)
<
0
)
{
perror
(
"Cannot send dump request"
);
exit
(
1
);
}
if
(
rtnl_dump_filter
(
&
rth
,
xfrm_state_keep
,
&
xb
,
NULL
,
NULL
)
<
0
)
{
fprintf
(
stderr
,
"Flush terminated
\n
"
);
exit
(
1
);
}
if
(
xb
.
nlmsg_count
==
0
)
{
if
(
show_stats
>
1
)
fprintf
(
stderr
,
"Flush completed
\n
"
);
break
;
}
if
(
rtnl_send
(
&
rth
,
xb
.
buf
,
xb
.
offset
)
<
0
)
{
perror
(
"Failed to send flush request
\n
"
);
exit
(
1
);
}
if
(
show_stats
>
1
)
fprintf
(
stderr
,
"Flushed nlmsg count = %d
\n
"
,
xb
.
nlmsg_count
);
xb
.
offset
=
0
;
xb
.
nlmsg_count
=
0
;
}
}
else
{
if
(
rtnl_wilddump_request
(
&
rth
,
preferred_family
,
XFRM_MSG_GETSA
)
<
0
)
{
perror
(
"Cannot send dump request"
);
exit
(
1
);
}
if
(
rtnl_dump_filter
(
&
rth
,
xfrm_state_print
,
stdout
,
NULL
,
NULL
)
<
0
)
{
fprintf
(
stderr
,
"Dump terminated
\n
"
);
exit
(
1
);
}
}
rtnl_close
(
&
rth
);
exit
(
0
);
}
int
do_xfrm_state
(
int
argc
,
char
**
argv
)
{
if
(
argc
<
1
)
return
xfrm_state_list_or_flush
(
0
,
NULL
,
0
);
#if 0
/*
* NLM_F_X is not supported for xfrm in the kernel.
*/
if (matches(*argv, "add") == 0)
return xfrm_state_modify(XFRM_MSG_NEWSA, NLM_F_CREATE|NLM_F_EXCL,
argc-1, argv+1);
if (matches(*argv, "change") == 0 || strcmp(*argv, "chg") == 0)
return xfrm_state_modify(XFRM_MSG_NEWSA, NLM_F_REPLACE,
argc-1, argv+1);
if (matches(*argv, "replace") == 0)
return xfrm_state_modify(XFRM_MSG_NEWSA, NLM_F_CREATE|NLM_F_REPLACE,
argc-1, argv+1);
if (matches(*argv, "prepend") == 0)
return xfrm_state_modify(XFRM_MSG_NEWSA, NLM_F_CREATE,
argc-1, argv+1);
if (matches(*argv, "append") == 0)
return xfrm_state_modify(XFRM_MSG_NEWSA, NLM_F_CREATE|NLM_F_APPEND,
argc-1, argv+1);
if (matches(*argv, "test") == 0)
return xfrm_state_modify(XFRM_MSG_NEWSA, NLM_F_EXCL,
argc-1, argv+1);
#else
if
(
matches
(
*
argv
,
"add"
)
==
0
)
return
xfrm_state_modify
(
XFRM_MSG_NEWSA
,
0
,
argc
-
1
,
argv
+
1
);
#endif
if
(
matches
(
*
argv
,
"update"
)
==
0
)
return
xfrm_state_modify
(
XFRM_MSG_UPDSA
,
0
,
argc
-
1
,
argv
+
1
);
if
(
matches
(
*
argv
,
"delete"
)
==
0
||
matches
(
*
argv
,
"del"
)
==
0
)
return
xfrm_state_get_or_delete
(
argc
-
1
,
argv
+
1
,
1
);
if
(
matches
(
*
argv
,
"list"
)
==
0
||
matches
(
*
argv
,
"show"
)
==
0
||
matches
(
*
argv
,
"lst"
)
==
0
)
return
xfrm_state_list_or_flush
(
argc
-
1
,
argv
+
1
,
0
);
if
(
matches
(
*
argv
,
"get"
)
==
0
)
return
xfrm_state_get_or_delete
(
argc
-
1
,
argv
+
1
,
0
);
if
(
matches
(
*
argv
,
"flush"
)
==
0
)
return
xfrm_state_list_or_flush
(
argc
-
1
,
argv
+
1
,
1
);
if
(
matches
(
*
argv
,
"help"
)
==
0
)
usage
();
fprintf
(
stderr
,
"Command
\"
%s
\"
is unknown, try
\"
ip xfrm state help
\"
.
\n
"
,
*
argv
);
exit
(
-
1
);
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment