-
Notifications
You must be signed in to change notification settings - Fork 1
/
tunnel.c
126 lines (102 loc) · 3.52 KB
/
tunnel.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/*
* tunnel.c : SCLP tunneling
*
* Copyright 2015 Ryota Kawashima <kawa1983@ieee.org> Nagoya Institute of Technology
*
* 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.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <linux/types.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/socket.h>
#include <linux/sclp.h>
#include <net/net_namespace.h>
#include <net/sclp_tunnel.h>
#include "sclp_impl.h"
#include "compat.h"
int sclp_sock_create4(struct net *net, struct sclp_port_cfg *cfg, struct socket **sockp)
{
int err;
struct socket *sock = NULL;
struct sockaddr_in sclp_addr;
err = sock_create_kern(AF_INET, SOCK_SCLP, 0, &sock);
if (err < 0)
goto error;
sk_change_net(sock->sk, net);
sclp_addr.sin_family = AF_INET;
sclp_addr.sin_addr = cfg->local_ip;
sclp_addr.sin_port = cfg->local_sclp_port;
err = kernel_bind(sock, (struct sockaddr*)&sclp_addr, sizeof(sclp_addr));
if (err < 0)
goto error;
if (cfg->peer_sclp_port) {
sclp_addr.sin_family = AF_INET;
sclp_addr.sin_addr = cfg->peer_ip;
sclp_addr.sin_port = cfg->peer_sclp_port;
err = kernel_connect(sock, (struct sockaddr*)&sclp_addr, sizeof(sclp_addr), 0);
if (err < 0)
goto error;
}
*sockp = sock;
return 0;
error:
if (sock) {
kernel_sock_shutdown(sock, SHUT_RDWR);
sk_release_kernel(sock->sk);
}
*sockp = NULL;
return err;
}
EXPORT_SYMBOL(sclp_sock_create4);
void setup_sclp_tunnel_sock(struct net *net, struct socket *sock,
struct sclp_tunnel_sock_cfg *cfg)
{
struct sock *sk = sock->sk;
/* Disable multicast loopback */
inet_sk(sk)->mc_loop = 0;
sclp_sk(sk)->encap_rcv = cfg->encap_rcv;
}
EXPORT_SYMBOL(setup_sclp_tunnel_sock);
int sclp_tunnel_xmit_skb(struct sk_buff *skb, struct rtable *rt,
__be32 daddr, __be32 saddr, __u8 tos, __u8 ttl,
__be16 df, __be16 dport, __be16 sport)
{
sclp_set_header(skb, dport, sport, sizeof(struct iphdr), compat_rt_dst(rt).dev->mtu);
skb->encapsulation = 0;
#if !defined(RHEL_RELEASE_CODE)
return iptunnel_xmit(skb->sk, rt, skb, saddr, daddr,
IPPROTO_SCLP, tos, ttl, df, false);
#elif RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7,0)
return iptunnel_xmit(skb->sk, rt, skb, saddr, daddr,
IPPROTO_SCLP, tos, ttl, df);
#else
{
struct iphdr *inner;
if (skb->protocol == htons(ETH_P_IP))
inner = ip_hdr(skb);
else
inner = NULL;
return iptunnel_xmit(sock_net(skb->sk), rt, skb, saddr, daddr,
IPPROTO_SCLP, tos, ttl, df, inner);
}
#endif
}
EXPORT_SYMBOL(sclp_tunnel_xmit_skb);
void sclp_tunnel_sock_release(struct socket *sock)
{
kernel_sock_shutdown(sock, SHUT_RDWR);
sk_release_kernel(sock->sk);
}
EXPORT_SYMBOL(sclp_tunnel_sock_release);