Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
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
nexedi
linux
Commits
f60dc6b1
Commit
f60dc6b1
authored
Nov 12, 2002
by
David S. Miller
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[IPSEC]: Netlink xfrm configuration interface.
parent
1b84cb26
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
1105 additions
and
22 deletions
+1105
-22
include/linux/in.h
include/linux/in.h
+1
-0
include/linux/xfrm.h
include/linux/xfrm.h
+50
-3
include/net/xfrm.h
include/net/xfrm.h
+0
-19
net/ipv4/Kconfig
net/ipv4/Kconfig
+8
-0
net/ipv4/Makefile
net/ipv4/Makefile
+1
-0
net/ipv4/ip_sockglue.c
net/ipv4/ip_sockglue.c
+1
-0
net/ipv4/xfrm_user.c
net/ipv4/xfrm_user.c
+1044
-0
No files found.
include/linux/in.h
View file @
f60dc6b1
...
...
@@ -70,6 +70,7 @@ struct in_addr {
#define IP_MTU 14
#define IP_FREEBIND 15
#define IP_IPSEC_POLICY 16
#define IP_XFRM_POLICY 17
/* BSD compatibility */
#define IP_RECVRETOPTS IP_RETOPTS
...
...
include/linux/xfrm.h
View file @
f60dc6b1
...
...
@@ -91,6 +91,22 @@ struct xfrm_stats {
__u32
integrity_failed
;
};
enum
{
XFRM_POLICY_IN
=
0
,
XFRM_POLICY_OUT
=
1
,
XFRM_POLICY_FWD
=
2
,
XFRM_POLICY_MAX
=
3
};
enum
{
XFRM_SHARE_ANY
,
/* No limitations */
XFRM_SHARE_SESSION
,
/* For this session only */
XFRM_SHARE_USER
,
/* For this user only */
XFRM_SHARE_UNIQUE
/* Use once */
};
/* Netlink configuration messages. */
#define XFRM_MSG_BASE 0x10
...
...
@@ -104,8 +120,9 @@ struct xfrm_stats {
#define XFRM_MSG_ALLOCSPI (RTM_BASE + 6)
#define XFRM_MSG_ACQUIRE (RTM_BASE + 7)
#define XFRM_MSG_EXPIRE (RTM_BASE + 8)
#define XFRM_MSG_MAX (XFRM_MSG_
ACQU
IRE+1)
#define XFRM_MSG_MAX (XFRM_MSG_
EXP
IRE+1)
struct
xfrm_user_tmpl
{
struct
xfrm_id
id
;
...
...
@@ -113,6 +130,7 @@ struct xfrm_user_tmpl {
__u16
reqid
;
__u8
mode
;
__u8
share
;
__u8
optional
;
__u32
aalgos
;
__u32
ealgos
;
__u32
calgos
;
...
...
@@ -135,9 +153,9 @@ struct xfrm_usersa_info {
struct
xfrm_lifetime_cfg
lft
;
struct
xfrm_lifetime_cur
curlft
;
struct
xfrm_stats
stats
;
__u32
seq
;
__u16
family
;
__u16
reqid
;
__u8
sa_type
;
__u8
mode
;
/* 0=transport,1=tunnel */
__u8
replay_window
;
};
...
...
@@ -148,15 +166,26 @@ struct xfrm_usersa_id {
__u8
proto
;
};
struct
xfrm_userspi_info
{
struct
xfrm_usersa_info
info
;
u32
min
;
u32
max
;
};
struct
xfrm_userpolicy_info
{
struct
xfrm_selector
sel
;
struct
xfrm_id
id
;
struct
xfrm_lifetime_cfg
lft
;
struct
xfrm_lifetime_cur
curlft
;
__u32
priority
;
__u32
index
;
__u16
family
;
__u8
dir
;
__u8
action
;
#define XFRM_POLICY_ALLOW 0
#define XFRM_POLICY_BLOCK 1
__u8
flags
;
#define XFRM_POLICY_LOCALOK 1
/* Allow user to override global policy */
__u8
share
;
};
struct
xfrm_userpolicy_id
{
...
...
@@ -165,4 +194,22 @@ struct xfrm_userpolicy_id {
__u8
dir
;
};
struct
xfrm_user_acquire
{
struct
xfrm_id
id
;
xfrm_address_t
saddr
;
struct
xfrm_userpolicy_info
policy
;
__u32
aalgos
;
__u32
ealgos
;
__u32
calgos
;
__u32
seq
;
};
struct
xfrm_user_expire
{
struct
xfrm_usersa_info
state
;
__u8
hard
;
};
#define XFRMGRP_ACQUIRE 1
#define XFRMGRP_EXPIRE 2
#endif
/* _LINUX_XFRM_H */
include/net/xfrm.h
View file @
f60dc6b1
...
...
@@ -187,22 +187,6 @@ struct xfrm_tmpl
#define XFRM_MAX_DEPTH 3
enum
{
XFRM_SHARE_ANY
,
/* No limitations */
XFRM_SHARE_SESSION
,
/* For this session only */
XFRM_SHARE_USER
,
/* For this user only */
XFRM_SHARE_UNIQUE
/* Use once */
};
enum
{
XFRM_POLICY_IN
=
0
,
XFRM_POLICY_OUT
=
1
,
XFRM_POLICY_FWD
=
2
,
XFRM_POLICY_MAX
=
3
};
struct
xfrm_policy
{
struct
xfrm_policy
*
next
;
...
...
@@ -218,10 +202,7 @@ struct xfrm_policy
struct
xfrm_lifetime_cur
curlft
;
struct
dst_entry
*
bundles
;
__u8
action
;
#define XFRM_POLICY_ALLOW 0
#define XFRM_POLICY_BLOCK 1
__u8
flags
;
#define XFRM_POLICY_LOCALOK 1
/* Allow user to override global policy */
__u8
dead
;
__u8
xfrm_nr
;
struct
xfrm_tmpl
xfrm_vec
[
XFRM_MAX_DEPTH
];
...
...
net/ipv4/Kconfig
View file @
f60dc6b1
...
...
@@ -362,5 +362,13 @@ config INET_ESP
If unsure, say Y.
config XFRM_USER
tristate "IP: IPsec user configuration interface"
---help---
Support for IPsec user configuration interface used
by native Linux tools.
If unsure, say Y.
source "net/ipv4/netfilter/Kconfig"
net/ipv4/Makefile
View file @
f60dc6b1
...
...
@@ -20,6 +20,7 @@ obj-$(CONFIG_INET_AH) += ah.o
obj-$(CONFIG_INET_ESP)
+=
esp.o
obj-$(CONFIG_IP_PNP)
+=
ipconfig.o
obj-$(CONFIG_NETFILTER)
+=
netfilter/
obj-$(CONFIG_XFRM_USER)
+=
xfrm_user.o
obj-y
+=
xfrm_policy.o xfrm_state.o xfrm_input.o
...
...
net/ipv4/ip_sockglue.c
View file @
f60dc6b1
...
...
@@ -626,6 +626,7 @@ int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int opt
break
;
case
IP_IPSEC_POLICY
:
case
IP_XFRM_POLICY
:
err
=
xfrm_user_policy
(
sk
,
optname
,
optval
,
optlen
);
break
;
...
...
net/ipv4/xfrm_user.c
0 → 100644
View file @
f60dc6b1
/* xfrm_user.c: User interface to configure xfrm engine.
*
* Copyright (C) 2002 David S. Miller (davem@redhat.com)
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/socket.h>
#include <linux/string.h>
#include <linux/net.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/pfkeyv2.h>
#include <linux/ipsec.h>
#include <linux/init.h>
#include <net/sock.h>
#include <net/xfrm.h>
static
struct
sock
*
xfrm_nl
;
static
int
verify_one_alg
(
struct
rtattr
**
xfrma
,
enum
xfrm_attr_type_t
type
)
{
struct
rtattr
*
rt
=
xfrma
[
type
];
struct
xfrm_algo
*
algp
;
if
(
!
rt
)
return
0
;
if
((
rt
->
rta_len
-
sizeof
(
*
rt
))
<
sizeof
(
*
algp
))
return
-
EINVAL
;
algp
=
RTA_DATA
(
rt
);
switch
(
type
)
{
case
XFRMA_ALG_AUTH
:
case
XFRMA_ALG_CRYPT
:
if
(
!
algp
->
alg_key_len
)
return
-
EINVAL
;
break
;
case
XFRMA_ALG_COMP
:
/* Zero length keys are legal. */
break
;
default:
return
-
EINVAL
;
};
algp
->
alg_name
[
CRYPTO_MAX_ALG_NAME
-
1
]
=
'\0'
;
return
0
;
}
static
int
verify_newsa_info
(
struct
xfrm_usersa_info
*
p
,
struct
rtattr
**
xfrma
)
{
int
err
;
err
=
-
EINVAL
;
switch
(
p
->
family
)
{
case
AF_INET
:
break
;
case
AF_INET6
:
/* XXX */
err
=
-
EAFNOSUPPORT
;
/* fallthru */
default:
goto
out
;
};
err
=
-
EINVAL
;
switch
(
p
->
id
.
proto
)
{
case
IPPROTO_AH
:
if
(
!
xfrma
[
XFRMA_ALG_AUTH
]
||
xfrma
[
XFRMA_ALG_CRYPT
]
||
xfrma
[
XFRMA_ALG_COMP
])
goto
out
;
break
;
case
IPPROTO_ESP
:
if
((
!
xfrma
[
XFRMA_ALG_AUTH
]
&&
!
xfrma
[
XFRMA_ALG_CRYPT
])
||
xfrma
[
XFRMA_ALG_COMP
])
goto
out
;
break
;
case
IPPROTO_COMP
:
if
(
!
xfrma
[
XFRMA_ALG_COMP
]
||
xfrma
[
XFRMA_ALG_AUTH
]
||
xfrma
[
XFRMA_ALG_CRYPT
])
goto
out
;
break
;
default:
goto
out
;
};
if
((
err
=
verify_one_alg
(
xfrma
,
XFRMA_ALG_AUTH
)))
goto
out
;
if
((
err
=
verify_one_alg
(
xfrma
,
XFRMA_ALG_CRYPT
)))
goto
out
;
if
((
err
=
verify_one_alg
(
xfrma
,
XFRMA_ALG_COMP
)))
goto
out
;
err
=
-
EINVAL
;
switch
(
p
->
mode
)
{
case
0
:
case
1
:
break
;
default:
goto
out
;
};
err
=
0
;
out:
return
err
;
}
static
int
attach_one_algo
(
struct
xfrm_algo
**
algpp
,
struct
rtattr
*
u_arg
)
{
struct
rtattr
*
rta
=
u_arg
;
struct
xfrm_algo
*
p
,
*
ualg
;
if
(
!
rta
)
return
0
;
ualg
=
RTA_DATA
(
rta
);
p
=
kmalloc
(
sizeof
(
*
ualg
)
+
ualg
->
alg_key_len
,
GFP_KERNEL
);
if
(
!
p
)
return
-
ENOMEM
;
memcpy
(
p
,
ualg
,
sizeof
(
*
ualg
)
+
ualg
->
alg_key_len
);
*
algpp
=
p
;
return
0
;
}
static
void
copy_from_user_state
(
struct
xfrm_state
*
x
,
struct
xfrm_usersa_info
*
p
)
{
memcpy
(
&
x
->
id
,
&
p
->
id
,
sizeof
(
x
->
id
));
memcpy
(
&
x
->
sel
,
&
p
->
sel
,
sizeof
(
x
->
sel
));
memcpy
(
&
x
->
lft
,
&
p
->
lft
,
sizeof
(
x
->
lft
));
x
->
props
.
mode
=
p
->
mode
;
x
->
props
.
replay_window
=
p
->
replay_window
;
x
->
props
.
reqid
=
p
->
reqid
;
x
->
props
.
saddr
=
x
->
sel
.
saddr
;
}
static
struct
xfrm_state
*
xfrm_state_construct
(
struct
xfrm_usersa_info
*
p
,
struct
rtattr
**
xfrma
,
int
*
errp
)
{
struct
xfrm_state
*
x
=
xfrm_state_alloc
();
int
err
=
-
ENOMEM
;
if
(
!
x
)
goto
error_no_put
;
copy_from_user_state
(
x
,
p
);
if
((
err
=
attach_one_algo
(
&
x
->
aalg
,
xfrma
[
XFRMA_ALG_AUTH
])))
goto
error
;
if
((
err
=
attach_one_algo
(
&
x
->
ealg
,
xfrma
[
XFRMA_ALG_CRYPT
])))
goto
error
;
if
((
err
=
attach_one_algo
(
&
x
->
calg
,
xfrma
[
XFRMA_ALG_COMP
])))
goto
error
;
err
=
-
ENOENT
;
x
->
type
=
xfrm_get_type
(
x
->
id
.
proto
);
if
(
x
->
type
==
NULL
)
goto
error
;
err
=
x
->
type
->
init_state
(
x
,
NULL
);
if
(
err
)
goto
error
;
x
->
curlft
.
add_time
=
(
unsigned
long
)
xtime
.
tv_sec
;
x
->
km
.
state
=
XFRM_STATE_VALID
;
x
->
km
.
seq
=
p
->
seq
;
return
x
;
error:
xfrm_state_put
(
x
);
error_no_put:
*
errp
=
err
;
return
NULL
;
}
static
int
xfrm_add_sa
(
struct
sk_buff
*
skb
,
struct
nlmsghdr
*
nlh
,
void
**
xfrma
)
{
struct
xfrm_usersa_info
*
p
=
NLMSG_DATA
(
nlh
);
struct
xfrm_state
*
x
,
*
x1
;
int
err
;
err
=
verify_newsa_info
(
p
,
(
struct
rtattr
**
)
xfrma
);
if
(
err
)
return
err
;
x
=
xfrm_state_construct
(
p
,
(
struct
rtattr
**
)
xfrma
,
&
err
);
if
(
!
x
)
return
err
;
x1
=
xfrm_state_lookup
(
x
->
props
.
saddr
.
xfrm4_addr
,
x
->
id
.
spi
,
x
->
id
.
proto
);
if
(
x1
)
{
xfrm_state_put
(
x
);
xfrm_state_put
(
x1
);
return
-
EEXIST
;
}
xfrm_state_insert
(
x
);
return
0
;
}
static
int
xfrm_del_sa
(
struct
sk_buff
*
skb
,
struct
nlmsghdr
*
nlh
,
void
**
xfrma
)
{
struct
xfrm_state
*
x
;
struct
xfrm_usersa_id
*
p
=
NLMSG_DATA
(
nlh
);
x
=
xfrm_state_lookup
(
p
->
saddr
.
xfrm4_addr
,
p
->
spi
,
p
->
proto
);
if
(
x
==
NULL
)
return
-
ESRCH
;
xfrm_state_delete
(
x
);
xfrm_state_put
(
x
);
return
0
;
}
static
void
copy_to_user_state
(
struct
xfrm_state
*
x
,
struct
xfrm_usersa_info
*
p
)
{
memcpy
(
&
p
->
id
,
&
x
->
id
,
sizeof
(
p
->
id
));
memcpy
(
&
p
->
sel
,
&
x
->
sel
,
sizeof
(
p
->
sel
));
memcpy
(
&
p
->
lft
,
&
x
->
lft
,
sizeof
(
p
->
lft
));
memcpy
(
&
p
->
curlft
,
&
x
->
curlft
,
sizeof
(
p
->
curlft
));
memcpy
(
&
p
->
stats
,
&
x
->
stats
,
sizeof
(
p
->
stats
));
p
->
mode
=
x
->
props
.
mode
;
p
->
replay_window
=
x
->
props
.
replay_window
;
p
->
reqid
=
x
->
props
.
reqid
;
p
->
seq
=
x
->
km
.
seq
;
}
struct
xfrm_dump_info
{
struct
sk_buff
*
in_skb
;
struct
sk_buff
*
out_skb
;
u32
nlmsg_seq
;
int
start_idx
;
int
this_idx
;
};
static
int
dump_one_state
(
struct
xfrm_state
*
x
,
int
count
,
void
*
ptr
)
{
struct
xfrm_dump_info
*
sp
=
ptr
;
struct
sk_buff
*
in_skb
=
sp
->
in_skb
;
struct
sk_buff
*
skb
=
sp
->
out_skb
;
struct
xfrm_usersa_info
*
p
;
struct
nlmsghdr
*
nlh
;
unsigned
char
*
b
=
skb
->
tail
;
if
(
sp
->
this_idx
<
sp
->
start_idx
)
goto
out
;
nlh
=
NLMSG_PUT
(
skb
,
NETLINK_CB
(
in_skb
).
pid
,
sp
->
nlmsg_seq
,
XFRM_MSG_NEWSA
,
sizeof
(
*
p
));
nlh
->
nlmsg_flags
=
0
;
p
=
NLMSG_DATA
(
nlh
);
copy_to_user_state
(
x
,
p
);
if
(
x
->
aalg
)
RTA_PUT
(
skb
,
XFRMA_ALG_AUTH
,
sizeof
(
*
(
x
->
aalg
)),
x
->
aalg
);
if
(
x
->
ealg
)
RTA_PUT
(
skb
,
XFRMA_ALG_CRYPT
,
sizeof
(
*
(
x
->
ealg
)),
x
->
ealg
);
if
(
x
->
calg
)
RTA_PUT
(
skb
,
XFRMA_ALG_COMP
,
sizeof
(
*
(
x
->
calg
)),
x
->
calg
);
nlh
->
nlmsg_len
=
skb
->
tail
-
b
;
out:
sp
->
this_idx
++
;
return
0
;
nlmsg_failure:
rtattr_failure:
skb_trim
(
skb
,
b
-
skb
->
data
);
return
-
1
;
}
static
int
xfrm_dump_sa
(
struct
sk_buff
*
skb
,
struct
netlink_callback
*
cb
)
{
struct
xfrm_dump_info
info
;
info
.
in_skb
=
cb
->
skb
;
info
.
out_skb
=
skb
;
info
.
nlmsg_seq
=
cb
->
nlh
->
nlmsg_seq
;
info
.
this_idx
=
0
;
info
.
start_idx
=
cb
->
args
[
0
];
(
void
)
xfrm_state_walk
(
IPSEC_PROTO_ANY
,
dump_one_state
,
&
info
);
cb
->
args
[
0
]
=
info
.
this_idx
;
return
skb
->
len
;
}
static
struct
sk_buff
*
xfrm_state_netlink
(
struct
sk_buff
*
in_skb
,
struct
xfrm_state
*
x
,
u32
seq
)
{
struct
xfrm_dump_info
info
;
struct
sk_buff
*
skb
;
skb
=
alloc_skb
(
NLMSG_GOODSIZE
,
GFP_ATOMIC
);
if
(
!
skb
)
return
ERR_PTR
(
-
ENOMEM
);
NETLINK_CB
(
skb
).
dst_pid
=
NETLINK_CB
(
in_skb
).
pid
;
info
.
in_skb
=
in_skb
;
info
.
out_skb
=
skb
;
info
.
nlmsg_seq
=
seq
;
info
.
this_idx
=
info
.
start_idx
=
0
;
if
(
dump_one_state
(
x
,
0
,
&
info
))
{
kfree_skb
(
skb
);
return
NULL
;
}
return
skb
;
}
static
int
xfrm_get_sa
(
struct
sk_buff
*
skb
,
struct
nlmsghdr
*
nlh
,
void
**
xfrma
)
{
struct
xfrm_usersa_id
*
p
=
NLMSG_DATA
(
nlh
);
struct
xfrm_state
*
x
;
struct
sk_buff
*
resp_skb
;
int
err
;
x
=
xfrm_state_lookup
(
p
->
saddr
.
xfrm4_addr
,
p
->
spi
,
p
->
proto
);
err
=
-
ESRCH
;
if
(
x
==
NULL
)
goto
out_noput
;
resp_skb
=
xfrm_state_netlink
(
skb
,
x
,
nlh
->
nlmsg_seq
);
if
(
IS_ERR
(
resp_skb
))
{
err
=
PTR_ERR
(
resp_skb
);
}
else
{
err
=
netlink_unicast
(
xfrm_nl
,
resp_skb
,
NETLINK_CB
(
skb
).
pid
,
MSG_DONTWAIT
);
}
xfrm_state_put
(
x
);
out_noput:
return
err
;
}
static
int
verify_userspi_info
(
struct
xfrm_userspi_info
*
p
)
{
switch
(
p
->
info
.
id
.
proto
)
{
case
IPPROTO_AH
:
case
IPPROTO_ESP
:
break
;
case
IPPROTO_COMP
:
/* IPCOMP spi is 16-bits. */
if
(
p
->
min
>=
0x10000
||
p
->
max
>=
0x10000
)
return
-
EINVAL
;
default:
return
-
EINVAL
;
};
if
(
p
->
min
>
p
->
max
)
return
-
EINVAL
;
return
0
;
}
static
int
xfrm_alloc_userspi
(
struct
sk_buff
*
skb
,
struct
nlmsghdr
*
nlh
,
void
**
xfrma
)
{
struct
xfrm_state
*
x
;
struct
xfrm_userspi_info
*
p
;
struct
sk_buff
*
resp_skb
;
int
err
;
p
=
NLMSG_DATA
(
nlh
);
err
=
verify_userspi_info
(
p
);
if
(
err
)
goto
out_noput
;
x
=
xfrm_find_acq
(
p
->
info
.
mode
,
p
->
info
.
reqid
,
p
->
info
.
id
.
proto
,
p
->
info
.
sel
.
daddr
.
xfrm4_addr
,
p
->
info
.
sel
.
saddr
.
xfrm4_addr
);
err
=
-
ENOENT
;
if
(
x
==
NULL
)
goto
out_noput
;
resp_skb
=
ERR_PTR
(
-
ENOENT
);
spin_lock_bh
(
&
x
->
lock
);
if
(
x
->
km
.
state
!=
XFRM_STATE_DEAD
)
{
xfrm_alloc_spi
(
x
,
p
->
min
,
p
->
max
);
if
(
x
->
id
.
spi
)
resp_skb
=
xfrm_state_netlink
(
skb
,
x
,
nlh
->
nlmsg_seq
);
}
spin_unlock_bh
(
&
x
->
lock
);
if
(
IS_ERR
(
resp_skb
))
{
err
=
PTR_ERR
(
resp_skb
);
goto
out
;
}
err
=
netlink_unicast
(
xfrm_nl
,
resp_skb
,
NETLINK_CB
(
skb
).
pid
,
MSG_DONTWAIT
);
out:
xfrm_state_put
(
x
);
out_noput:
return
err
;
}
static
int
verify_policy_dir
(
__u8
dir
)
{
switch
(
dir
)
{
case
XFRM_POLICY_IN
:
case
XFRM_POLICY_OUT
:
case
XFRM_POLICY_FWD
:
break
;
default:
return
-
EINVAL
;
};
return
0
;
}
static
int
verify_newpolicy_info
(
struct
xfrm_userpolicy_info
*
p
)
{
switch
(
p
->
share
)
{
case
XFRM_SHARE_ANY
:
case
XFRM_SHARE_SESSION
:
case
XFRM_SHARE_USER
:
case
XFRM_SHARE_UNIQUE
:
break
;
default:
return
-
EINVAL
;
};
switch
(
p
->
action
)
{
case
XFRM_POLICY_ALLOW
:
case
XFRM_POLICY_BLOCK
:
break
;
default:
return
-
EINVAL
;
};
return
verify_policy_dir
(
p
->
dir
);
}
static
void
copy_templates
(
struct
xfrm_policy
*
xp
,
struct
xfrm_user_tmpl
*
ut
,
int
nr
)
{
int
i
;
xp
->
xfrm_nr
=
nr
;
for
(
i
=
0
;
i
<
nr
;
i
++
,
ut
++
)
{
struct
xfrm_tmpl
*
t
=
&
xp
->
xfrm_vec
[
i
];
memcpy
(
&
t
->
id
,
&
ut
->
id
,
sizeof
(
struct
xfrm_id
));
memcpy
(
&
t
->
saddr
,
&
ut
->
saddr
,
sizeof
(
xfrm_address_t
));
t
->
reqid
=
ut
->
reqid
;
t
->
mode
=
ut
->
mode
;
t
->
share
=
ut
->
share
;
t
->
optional
=
ut
->
optional
;
t
->
aalgos
=
ut
->
aalgos
;
t
->
ealgos
=
ut
->
ealgos
;
t
->
calgos
=
ut
->
calgos
;
}
}
static
int
copy_user_tmpl
(
struct
xfrm_policy
*
pol
,
struct
rtattr
**
xfrma
)
{
struct
rtattr
*
rt
=
xfrma
[
XFRMA_TMPL
];
struct
xfrm_user_tmpl
*
utmpl
;
int
nr
;
if
(
!
rt
)
{
pol
->
xfrm_nr
=
0
;
}
else
{
nr
=
(
rt
->
rta_len
-
sizeof
(
*
rt
))
/
sizeof
(
*
utmpl
);
if
(
nr
>
XFRM_MAX_DEPTH
)
return
-
EINVAL
;
copy_templates
(
pol
,
RTA_DATA
(
rt
),
nr
);
}
return
0
;
}
static
void
copy_from_user_policy
(
struct
xfrm_policy
*
xp
,
struct
xfrm_userpolicy_info
*
p
)
{
xp
->
priority
=
p
->
priority
;
xp
->
index
=
p
->
index
;
memcpy
(
&
xp
->
selector
,
&
p
->
sel
,
sizeof
(
xp
->
selector
));
memcpy
(
&
xp
->
lft
,
&
p
->
lft
,
sizeof
(
xp
->
lft
));
xp
->
action
=
p
->
action
;
xp
->
flags
=
p
->
flags
;
/* XXX xp->family = p->family; */
/* XXX xp->share = p->share; */
}
static
void
copy_to_user_policy
(
struct
xfrm_policy
*
xp
,
struct
xfrm_userpolicy_info
*
p
,
int
dir
)
{
memcpy
(
&
p
->
sel
,
&
xp
->
selector
,
sizeof
(
p
->
sel
));
memcpy
(
&
p
->
lft
,
&
xp
->
lft
,
sizeof
(
p
->
lft
));
memcpy
(
&
p
->
curlft
,
&
xp
->
curlft
,
sizeof
(
p
->
curlft
));
p
->
priority
=
xp
->
priority
;
p
->
index
=
xp
->
index
;
p
->
family
=
AF_INET
;
/* XXX xp->family */
p
->
dir
=
dir
;
p
->
action
=
xp
->
action
;
p
->
flags
=
xp
->
flags
;
p
->
share
=
XFRM_SHARE_ANY
;
/* XXX xp->share */
}
static
struct
xfrm_policy
*
xfrm_policy_construct
(
struct
xfrm_userpolicy_info
*
p
,
struct
rtattr
**
xfrma
,
int
*
errp
)
{
struct
xfrm_policy
*
xp
=
xfrm_policy_alloc
(
GFP_KERNEL
);
int
err
;
if
(
!
xp
)
{
*
errp
=
-
ENOMEM
;
return
NULL
;
}
copy_from_user_policy
(
xp
,
p
);
err
=
copy_user_tmpl
(
xp
,
xfrma
);
if
(
err
)
{
*
errp
=
err
;
kfree
(
xp
);
xp
=
NULL
;
}
return
xp
;
}
static
int
xfrm_add_policy
(
struct
sk_buff
*
skb
,
struct
nlmsghdr
*
nlh
,
void
**
xfrma
)
{
struct
xfrm_userpolicy_info
*
p
=
NLMSG_DATA
(
nlh
);
struct
xfrm_policy
*
xp
;
int
err
;
err
=
verify_newpolicy_info
(
p
);
if
(
err
)
return
err
;
xp
=
xfrm_policy_construct
(
p
,
(
struct
rtattr
**
)
xfrma
,
&
err
);
if
(
!
xp
)
return
err
;
err
=
xfrm_policy_insert
(
p
->
dir
,
xp
,
1
);
if
(
err
)
{
kfree
(
xp
);
return
err
;
}
xfrm_pol_put
(
xp
);
return
0
;
}
static
int
xfrm_del_policy
(
struct
sk_buff
*
skb
,
struct
nlmsghdr
*
nlh
,
void
**
xfrma
)
{
struct
xfrm_policy
*
xp
;
struct
xfrm_userpolicy_id
*
p
;
int
err
;
p
=
NLMSG_DATA
(
nlh
);
err
=
verify_policy_dir
(
p
->
dir
);
if
(
err
)
return
err
;
xp
=
xfrm_policy_delete
(
p
->
dir
,
&
p
->
sel
);
if
(
xp
==
NULL
)
return
-
ENOENT
;
xfrm_policy_kill
(
xp
);
xfrm_pol_put
(
xp
);
return
0
;
}
static
int
dump_one_policy
(
struct
xfrm_policy
*
xp
,
int
dir
,
int
count
,
void
*
ptr
)
{
struct
xfrm_dump_info
*
sp
=
ptr
;
struct
xfrm_userpolicy_info
*
p
;
struct
sk_buff
*
in_skb
=
sp
->
in_skb
;
struct
sk_buff
*
skb
=
sp
->
out_skb
;
struct
nlmsghdr
*
nlh
;
unsigned
char
*
b
=
skb
->
tail
;
if
(
sp
->
this_idx
<
sp
->
start_idx
)
goto
out
;
nlh
=
NLMSG_PUT
(
skb
,
NETLINK_CB
(
in_skb
).
pid
,
sp
->
nlmsg_seq
,
XFRM_MSG_NEWPOLICY
,
sizeof
(
*
p
));
p
=
NLMSG_DATA
(
nlh
);
nlh
->
nlmsg_flags
=
0
;
copy_to_user_policy
(
xp
,
p
,
dir
);
if
(
xp
->
xfrm_nr
)
{
struct
xfrm_user_tmpl
vec
[
XFRM_MAX_DEPTH
];
int
i
;
for
(
i
=
0
;
i
<
xp
->
xfrm_nr
;
i
++
)
{
struct
xfrm_user_tmpl
*
up
=
&
vec
[
i
];
struct
xfrm_tmpl
*
kp
=
&
xp
->
xfrm_vec
[
i
];
memcpy
(
&
up
->
id
,
&
kp
->
id
,
sizeof
(
up
->
id
));
memcpy
(
&
up
->
saddr
,
&
kp
->
saddr
,
sizeof
(
up
->
saddr
));
up
->
reqid
=
kp
->
reqid
;
up
->
mode
=
kp
->
mode
;
up
->
share
=
kp
->
share
;
up
->
optional
=
kp
->
optional
;
up
->
aalgos
=
kp
->
aalgos
;
up
->
ealgos
=
kp
->
ealgos
;
up
->
calgos
=
kp
->
calgos
;
}
RTA_PUT
(
skb
,
XFRMA_TMPL
,
(
sizeof
(
struct
xfrm_user_tmpl
)
*
xp
->
xfrm_nr
),
vec
);
}
nlh
->
nlmsg_len
=
skb
->
tail
-
b
;
out:
sp
->
this_idx
++
;
return
0
;
nlmsg_failure:
rtattr_failure:
skb_trim
(
skb
,
b
-
skb
->
data
);
return
-
1
;
}
static
int
xfrm_dump_policy
(
struct
sk_buff
*
skb
,
struct
netlink_callback
*
cb
)
{
struct
xfrm_dump_info
info
;
info
.
in_skb
=
cb
->
skb
;
info
.
out_skb
=
skb
;
info
.
nlmsg_seq
=
cb
->
nlh
->
nlmsg_seq
;
info
.
start_idx
=
cb
->
args
[
0
];
(
void
)
xfrm_policy_walk
(
dump_one_policy
,
&
info
);
cb
->
args
[
0
]
=
info
.
this_idx
;
return
skb
->
len
;
}
static
struct
sk_buff
*
xfrm_policy_netlink
(
struct
sk_buff
*
in_skb
,
struct
xfrm_policy
*
xp
,
int
dir
,
u32
seq
)
{
struct
xfrm_dump_info
info
;
struct
sk_buff
*
skb
;
skb
=
alloc_skb
(
NLMSG_GOODSIZE
,
GFP_KERNEL
);
if
(
!
skb
)
return
ERR_PTR
(
-
ENOMEM
);
NETLINK_CB
(
skb
).
dst_pid
=
NETLINK_CB
(
in_skb
).
pid
;
info
.
in_skb
=
in_skb
;
info
.
out_skb
=
skb
;
info
.
nlmsg_seq
=
seq
;
info
.
this_idx
=
info
.
start_idx
=
0
;
if
(
dump_one_policy
(
xp
,
dir
,
0
,
&
info
)
<
0
)
{
kfree_skb
(
skb
);
return
NULL
;
}
return
skb
;
}
static
int
xfrm_get_policy
(
struct
sk_buff
*
skb
,
struct
nlmsghdr
*
nlh
,
void
**
xfrma
)
{
struct
xfrm_policy
*
xp
;
struct
xfrm_userpolicy_id
*
p
;
struct
sk_buff
*
resp_skb
;
int
err
;
p
=
NLMSG_DATA
(
nlh
);
xp
=
xfrm_policy_byid
(
p
->
dir
,
p
->
index
,
0
);
if
(
xp
==
NULL
)
return
-
ENOENT
;
resp_skb
=
xfrm_policy_netlink
(
skb
,
xp
,
p
->
dir
,
nlh
->
nlmsg_seq
);
if
(
IS_ERR
(
resp_skb
))
{
err
=
PTR_ERR
(
resp_skb
);
}
else
{
err
=
netlink_unicast
(
xfrm_nl
,
resp_skb
,
NETLINK_CB
(
skb
).
pid
,
MSG_DONTWAIT
);
}
xfrm_pol_put
(
xp
);
return
err
;
}
static
const
int
xfrm_msg_min
[(
XFRM_MSG_MAX
+
1
-
XFRM_MSG_BASE
)]
=
{
NLMSG_LENGTH
(
sizeof
(
struct
xfrm_usersa_info
)),
/* NEW SA */
NLMSG_LENGTH
(
sizeof
(
struct
xfrm_usersa_id
)),
/* DEL SA */
NLMSG_LENGTH
(
sizeof
(
struct
xfrm_usersa_id
)),
/* GET SA */
NLMSG_LENGTH
(
sizeof
(
struct
xfrm_userpolicy_info
)),
/* NEW POLICY */
NLMSG_LENGTH
(
sizeof
(
struct
xfrm_userpolicy_id
)),
/* DEL POLICY */
NLMSG_LENGTH
(
sizeof
(
struct
xfrm_userpolicy_id
)),
/* GET POLICY */
NLMSG_LENGTH
(
sizeof
(
struct
xfrm_userspi_info
)),
/* ALLOC SPI */
NLMSG_LENGTH
(
sizeof
(
struct
xfrm_user_acquire
)),
/* ACQUIRE */
NLMSG_LENGTH
(
sizeof
(
struct
xfrm_user_expire
)),
/* EXPIRE */
};
static
struct
xfrm_link
{
int
(
*
doit
)(
struct
sk_buff
*
,
struct
nlmsghdr
*
,
void
**
);
int
(
*
dump
)(
struct
sk_buff
*
,
struct
netlink_callback
*
);
}
xfrm_dispatch
[]
=
{
{
.
doit
=
xfrm_add_sa
,
},
{
.
doit
=
xfrm_del_sa
,
},
{
.
doit
=
xfrm_get_sa
,
.
dump
=
xfrm_dump_sa
,
},
{
.
doit
=
xfrm_add_policy
},
{
.
doit
=
xfrm_del_policy
},
{
.
doit
=
xfrm_get_policy
,
.
dump
=
xfrm_dump_policy
,
},
{
.
doit
=
xfrm_alloc_userspi
},
};
static
int
xfrm_done
(
struct
netlink_callback
*
cb
)
{
return
0
;
}
static
int
xfrm_user_rcv_msg
(
struct
sk_buff
*
skb
,
struct
nlmsghdr
*
nlh
,
int
*
errp
)
{
struct
rtattr
*
xfrma
[
XFRMA_MAX
];
struct
xfrm_link
*
link
;
int
type
,
min_len
,
kind
;
if
(
!
(
nlh
->
nlmsg_flags
&
NLM_F_REQUEST
))
return
0
;
type
=
nlh
->
nlmsg_type
;
/* A control message: ignore them */
if
(
type
<
XFRM_MSG_BASE
)
return
0
;
/* Unknown message: reply with EINVAL */
if
(
type
>
XFRM_MSG_MAX
)
goto
err_einval
;
type
-=
XFRM_MSG_BASE
;
kind
=
(
type
&
3
);
link
=
&
xfrm_dispatch
[
type
];
/* All operations require privileges, even GET */
if
(
!
cap_raised
(
NETLINK_CB
(
skb
).
eff_cap
,
CAP_NET_ADMIN
))
{
*
errp
=
-
EPERM
;
return
-
1
;
}
if
(
kind
==
2
&&
(
nlh
->
nlmsg_flags
&
NLM_F_DUMP
))
{
u32
rlen
;
if
(
link
->
dump
==
NULL
)
goto
err_einval
;
if
((
*
errp
=
netlink_dump_start
(
xfrm_nl
,
skb
,
nlh
,
link
->
dump
,
xfrm_done
))
!=
0
)
{
return
-
1
;
}
rlen
=
NLMSG_ALIGN
(
nlh
->
nlmsg_len
);
if
(
rlen
>
skb
->
len
)
rlen
=
skb
->
len
;
skb_pull
(
skb
,
rlen
);
return
-
1
;
}
memset
(
xfrma
,
0
,
sizeof
(
xfrma
));
if
(
nlh
->
nlmsg_len
<
(
min_len
=
xfrm_msg_min
[
type
]))
goto
err_einval
;
if
(
nlh
->
nlmsg_len
>
min_len
)
{
int
attrlen
=
nlh
->
nlmsg_len
-
NLMSG_ALIGN
(
min_len
);
struct
rtattr
*
attr
=
(
void
*
)
nlh
+
NLMSG_ALIGN
(
min_len
);
while
(
RTA_OK
(
attr
,
attrlen
))
{
unsigned
short
flavor
=
attr
->
rta_type
;
if
(
flavor
)
{
if
(
flavor
>
XFRMA_MAX
)
goto
err_einval
;
xfrma
[
flavor
-
1
]
=
attr
;
}
attr
=
RTA_NEXT
(
attr
,
attrlen
);
}
}
if
(
link
->
doit
==
NULL
)
goto
err_einval
;
*
errp
=
link
->
doit
(
skb
,
nlh
,
(
void
**
)
&
xfrma
);
return
*
errp
;
err_einval:
*
errp
=
-
EINVAL
;
return
-
1
;
}
static
int
xfrm_user_rcv_skb
(
struct
sk_buff
*
skb
)
{
int
err
;
struct
nlmsghdr
*
nlh
;
while
(
skb
->
len
>=
NLMSG_SPACE
(
0
))
{
u32
rlen
;
nlh
=
(
struct
nlmsghdr
*
)
skb
->
data
;
if
(
nlh
->
nlmsg_len
<
sizeof
(
*
nlh
)
||
skb
->
len
<
nlh
->
nlmsg_len
)
return
0
;
rlen
=
NLMSG_ALIGN
(
nlh
->
nlmsg_len
);
if
(
rlen
>
skb
->
len
)
rlen
=
skb
->
len
;
if
(
xfrm_user_rcv_msg
(
skb
,
nlh
,
&
err
))
{
if
(
err
==
0
)
return
-
1
;
netlink_ack
(
skb
,
nlh
,
err
);
}
else
if
(
nlh
->
nlmsg_flags
&
NLM_F_ACK
)
netlink_ack
(
skb
,
nlh
,
0
);
skb_pull
(
skb
,
rlen
);
}
return
0
;
}
static
void
xfrm_netlink_rcv
(
struct
sock
*
sk
,
int
len
)
{
do
{
struct
sk_buff
*
skb
;
down
(
&
xfrm_cfg_sem
);
while
((
skb
=
skb_dequeue
(
&
sk
->
receive_queue
))
!=
NULL
)
{
if
(
xfrm_user_rcv_skb
(
skb
))
{
if
(
skb
->
len
)
skb_queue_head
(
&
sk
->
receive_queue
,
skb
);
else
kfree_skb
(
skb
);
break
;
}
kfree_skb
(
skb
);
}
up
(
&
xfrm_cfg_sem
);
}
while
(
xfrm_nl
&&
xfrm_nl
->
receive_queue
.
qlen
);
}
static
int
build_expire
(
struct
sk_buff
*
skb
,
struct
xfrm_state
*
x
,
int
hard
)
{
struct
xfrm_user_expire
*
ue
;
struct
nlmsghdr
*
nlh
;
unsigned
char
*
b
=
skb
->
tail
;
nlh
=
NLMSG_PUT
(
skb
,
0
,
0
,
XFRM_MSG_EXPIRE
,
sizeof
(
*
ue
));
ue
=
NLMSG_DATA
(
nlh
);
nlh
->
nlmsg_flags
=
0
;
copy_to_user_state
(
x
,
&
ue
->
state
);
ue
->
hard
=
(
hard
!=
0
)
?
1
:
0
;
nlh
->
nlmsg_len
=
skb
->
tail
-
b
;
return
skb
->
len
;
nlmsg_failure:
skb_trim
(
skb
,
b
-
skb
->
data
);
return
-
1
;
}
static
int
xfrm_send_notify
(
struct
xfrm_state
*
x
,
int
hard
)
{
struct
sk_buff
*
skb
;
skb
=
alloc_skb
(
sizeof
(
struct
xfrm_user_expire
)
+
16
,
GFP_ATOMIC
);
if
(
skb
==
NULL
)
return
-
ENOMEM
;
if
(
build_expire
(
skb
,
x
,
hard
)
<
0
)
BUG
();
NETLINK_CB
(
skb
).
dst_groups
=
XFRMGRP_EXPIRE
;
netlink_broadcast
(
xfrm_nl
,
skb
,
0
,
XFRMGRP_EXPIRE
,
GFP_ATOMIC
);
return
0
;
}
/* XXX Make this xfrm_state.c:xfrm_get_acqseq() */
static
u32
get_acqseq
(
void
)
{
u32
res
;
static
u32
acqseq
;
static
spinlock_t
acqseq_lock
=
SPIN_LOCK_UNLOCKED
;
spin_lock_bh
(
&
acqseq_lock
);
res
=
(
++
acqseq
?
:
++
acqseq
);
spin_unlock_bh
(
&
acqseq_lock
);
return
res
;
}
static
int
build_acquire
(
struct
sk_buff
*
skb
,
struct
xfrm_state
*
x
,
struct
xfrm_tmpl
*
xt
,
struct
xfrm_policy
*
xp
,
int
dir
)
{
struct
xfrm_user_acquire
*
ua
;
struct
nlmsghdr
*
nlh
;
unsigned
char
*
b
=
skb
->
tail
;
__u32
seq
=
get_acqseq
();
nlh
=
NLMSG_PUT
(
skb
,
0
,
0
,
XFRM_MSG_ACQUIRE
,
sizeof
(
*
ua
));
ua
=
NLMSG_DATA
(
nlh
);
nlh
->
nlmsg_flags
=
0
;
memcpy
(
&
ua
->
id
,
&
x
->
id
,
sizeof
(
ua
->
id
));
memcpy
(
&
ua
->
saddr
,
&
x
->
props
.
saddr
,
sizeof
(
ua
->
saddr
));
copy_to_user_policy
(
xp
,
&
ua
->
policy
,
dir
);
ua
->
aalgos
=
xt
->
aalgos
;
ua
->
ealgos
=
xt
->
ealgos
;
ua
->
calgos
=
xt
->
calgos
;
ua
->
seq
=
x
->
km
.
seq
=
seq
;
nlh
->
nlmsg_len
=
skb
->
tail
-
b
;
return
skb
->
len
;
nlmsg_failure:
skb_trim
(
skb
,
b
-
skb
->
data
);
return
-
1
;
}
static
int
xfrm_send_acquire
(
struct
xfrm_state
*
x
,
struct
xfrm_tmpl
*
xt
,
struct
xfrm_policy
*
xp
,
int
dir
)
{
struct
sk_buff
*
skb
;
skb
=
alloc_skb
(
sizeof
(
struct
xfrm_user_acquire
)
+
16
,
GFP_ATOMIC
);
if
(
skb
==
NULL
)
return
-
ENOMEM
;
if
(
build_acquire
(
skb
,
x
,
xt
,
xp
,
dir
)
<
0
)
BUG
();
NETLINK_CB
(
skb
).
dst_groups
=
XFRMGRP_ACQUIRE
;
netlink_broadcast
(
xfrm_nl
,
skb
,
0
,
XFRMGRP_ACQUIRE
,
GFP_ATOMIC
);
return
0
;
}
/* User gives us xfrm_user_policy_info followed by an array of 0
* or more templates.
*/
struct
xfrm_policy
*
xfrm_compile_policy
(
int
opt
,
u8
*
data
,
int
len
,
int
*
dir
)
{
struct
xfrm_userpolicy_info
*
p
=
(
struct
xfrm_userpolicy_info
*
)
data
;
struct
xfrm_user_tmpl
*
ut
=
(
struct
xfrm_user_tmpl
*
)
(
p
+
1
);
struct
xfrm_policy
*
xp
;
int
nr
;
if
(
opt
!=
IP_XFRM_POLICY
)
{
*
dir
=
-
EOPNOTSUPP
;
return
NULL
;
}
*
dir
=
-
EINVAL
;
if
(
len
<
sizeof
(
*
p
)
||
verify_newpolicy_info
(
p
))
return
NULL
;
nr
=
((
len
-
sizeof
(
*
p
))
/
sizeof
(
*
ut
));
if
(
nr
>
XFRM_MAX_DEPTH
)
return
NULL
;
xp
=
xfrm_policy_alloc
(
GFP_KERNEL
);
if
(
xp
==
NULL
)
{
*
dir
=
-
ENOBUFS
;
return
NULL
;
}
copy_from_user_policy
(
xp
,
p
);
copy_templates
(
xp
,
ut
,
nr
);
*
dir
=
p
->
dir
;
return
xp
;
}
static
struct
xfrm_mgr
netlink_mgr
=
{
.
id
=
"netlink"
,
.
notify
=
xfrm_send_notify
,
.
acquire
=
xfrm_send_acquire
,
.
compile_policy
=
xfrm_compile_policy
,
};
static
int
__init
xfrm_user_init
(
void
)
{
printk
(
KERN_INFO
"Initializing IPsec netlink socket
\n
"
);
xfrm_nl
=
netlink_kernel_create
(
NETLINK_XFRM
,
xfrm_netlink_rcv
);
if
(
xfrm_nl
==
NULL
)
panic
(
"xfrm_user_init: cannot initialize xfrm_nl
\n
"
);
xfrm_register_km
(
&
netlink_mgr
);
return
0
;
}
static
void
__exit
xfrm_user_exit
(
void
)
{
xfrm_unregister_km
(
&
netlink_mgr
);
/* XXX need netlink_kernel_destroy XXX */
}
module_init
(
xfrm_user_init
);
module_exit
(
xfrm_user_exit
);
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