]>
Commit | Line | Data |
---|---|---|
4237026e PS |
1 | /* |
2 | * Copyright (c) 2015 Nicira, Inc. | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or | |
5 | * modify it under the terms of the GNU General Public License | |
6 | * as published by the Free Software Foundation; either version | |
7 | * 2 of the License, or (at your option) any later version. | |
8 | */ | |
9 | ||
10 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | |
11 | ||
12 | #include <linux/if_vlan.h> | |
13 | #include <linux/in.h> | |
14 | #include <linux/ip.h> | |
15 | #include <linux/module.h> | |
16 | #include <linux/net.h> | |
17 | #include <linux/rculist.h> | |
18 | #include <linux/udp.h> | |
19 | ||
20 | #include <net/icmp.h> | |
21 | #include <net/ip.h> | |
22 | #include <net/route.h> | |
23 | #include <net/stt.h> | |
24 | #include <net/udp.h> | |
25 | ||
26 | #include "datapath.h" | |
27 | #include "vport.h" | |
28 | ||
29 | #ifdef OVS_STT | |
30 | static struct vport_ops ovs_stt_vport_ops; | |
31 | ||
32 | /** | |
33 | * struct stt_port | |
34 | * @stt_sock: The socket created for this port number. | |
35 | * @name: vport name. | |
36 | */ | |
37 | struct stt_port { | |
38 | struct stt_sock *stt_sock; | |
39 | char name[IFNAMSIZ]; | |
40 | }; | |
41 | ||
42 | static inline struct stt_port *stt_vport(const struct vport *vport) | |
43 | { | |
44 | return vport_priv(vport); | |
45 | } | |
46 | ||
47 | static void stt_rcv(struct stt_sock *stt_sock, struct sk_buff *skb) | |
48 | { | |
49 | struct vport *vport = stt_sock->rcv_data; | |
50 | struct stthdr *stth = stt_hdr(skb); | |
51 | struct ovs_tunnel_info tun_info; | |
52 | struct sk_buff *next; | |
53 | ||
54 | ovs_flow_tun_info_init(&tun_info, ip_hdr(skb), | |
55 | tcp_hdr(skb)->source, tcp_hdr(skb)->dest, | |
56 | get_unaligned(&stth->key), | |
57 | TUNNEL_KEY | TUNNEL_CSUM, | |
58 | NULL, 0); | |
59 | do { | |
60 | next = skb->next; | |
61 | skb->next = NULL; | |
62 | ovs_vport_receive(vport, skb, &tun_info); | |
63 | } while ((skb = next)); | |
64 | } | |
65 | ||
66 | static int stt_tnl_get_options(const struct vport *vport, | |
67 | struct sk_buff *skb) | |
68 | { | |
69 | struct stt_port *stt_port = stt_vport(vport); | |
70 | struct inet_sock *sk = inet_sk(stt_port->stt_sock->sock->sk); | |
71 | ||
72 | if (nla_put_u16(skb, OVS_TUNNEL_ATTR_DST_PORT, ntohs(sk->inet_sport))) | |
73 | return -EMSGSIZE; | |
74 | return 0; | |
75 | } | |
76 | ||
77 | static void stt_tnl_destroy(struct vport *vport) | |
78 | { | |
79 | struct stt_port *stt_port = stt_vport(vport); | |
80 | ||
81 | stt_sock_release(stt_port->stt_sock); | |
82 | ovs_vport_deferred_free(vport); | |
83 | } | |
84 | ||
85 | static struct vport *stt_tnl_create(const struct vport_parms *parms) | |
86 | { | |
87 | struct net *net = ovs_dp_get_net(parms->dp); | |
88 | struct nlattr *options = parms->options; | |
89 | struct stt_port *stt_port; | |
90 | struct stt_sock *stt_sock; | |
91 | struct vport *vport; | |
92 | struct nlattr *a; | |
93 | int err; | |
94 | u16 dst_port; | |
95 | ||
96 | if (!options) { | |
97 | err = -EINVAL; | |
98 | goto error; | |
99 | } | |
100 | ||
101 | a = nla_find_nested(options, OVS_TUNNEL_ATTR_DST_PORT); | |
102 | if (a && nla_len(a) == sizeof(u16)) { | |
103 | dst_port = nla_get_u16(a); | |
104 | } else { | |
105 | /* Require destination port from userspace. */ | |
106 | err = -EINVAL; | |
107 | goto error; | |
108 | } | |
109 | ||
110 | vport = ovs_vport_alloc(sizeof(struct stt_port), | |
111 | &ovs_stt_vport_ops, parms); | |
112 | if (IS_ERR(vport)) | |
113 | return vport; | |
114 | ||
115 | stt_port = stt_vport(vport); | |
116 | strncpy(stt_port->name, parms->name, IFNAMSIZ); | |
117 | ||
118 | stt_sock = stt_sock_add(net, htons(dst_port), stt_rcv, vport); | |
119 | if (IS_ERR(stt_sock)) { | |
120 | ovs_vport_free(vport); | |
121 | return ERR_CAST(stt_sock); | |
122 | } | |
123 | stt_port->stt_sock = stt_sock; | |
124 | ||
125 | return vport; | |
126 | error: | |
127 | return ERR_PTR(err); | |
128 | } | |
129 | ||
130 | static int stt_tnl_send(struct vport *vport, struct sk_buff *skb) | |
131 | { | |
132 | struct net *net = ovs_dp_get_net(vport->dp); | |
133 | struct stt_port *stt_port = stt_vport(vport); | |
134 | __be16 dport = inet_sk(stt_port->stt_sock->sock->sk)->inet_sport; | |
135 | const struct ovs_key_ipv4_tunnel *tun_key; | |
136 | const struct ovs_tunnel_info *tun_info; | |
137 | struct rtable *rt; | |
138 | __be16 sport; | |
139 | __be32 saddr; | |
140 | __be16 df; | |
141 | int err; | |
142 | ||
143 | tun_info = OVS_CB(skb)->egress_tun_info; | |
144 | if (unlikely(!tun_info)) { | |
145 | err = -EINVAL; | |
146 | goto error; | |
147 | } | |
148 | ||
149 | tun_key = &tun_info->tunnel; | |
150 | /* Route lookup */ | |
151 | saddr = tun_key->ipv4_src; | |
152 | rt = find_route(ovs_dp_get_net(vport->dp), | |
153 | &saddr, tun_key->ipv4_dst, | |
154 | IPPROTO_TCP, tun_key->ipv4_tos, | |
155 | skb->mark); | |
156 | ||
157 | if (IS_ERR(rt)) { | |
158 | err = PTR_ERR(rt); | |
159 | goto error; | |
160 | } | |
161 | ||
162 | df = tun_key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; | |
163 | sport = udp_flow_src_port(net, skb, 1, USHRT_MAX, true); | |
164 | skb->ignore_df = 1; | |
165 | ||
166 | return stt_xmit_skb(skb, rt, saddr, tun_key->ipv4_dst, | |
167 | tun_key->ipv4_tos, tun_key->ipv4_ttl, | |
168 | df, sport, dport, tun_key->tun_id); | |
169 | error: | |
170 | kfree_skb(skb); | |
171 | return err; | |
172 | } | |
173 | ||
174 | static const char *stt_tnl_get_name(const struct vport *vport) | |
175 | { | |
176 | return stt_vport(vport)->name; | |
177 | } | |
178 | ||
179 | static int stt_get_egress_tun_info(struct vport *vport, struct sk_buff *skb, | |
180 | struct ovs_tunnel_info *egress_tun_info) | |
181 | { | |
182 | struct stt_port *stt_port = stt_vport(vport); | |
183 | struct net *net = ovs_dp_get_net(vport->dp); | |
184 | __be16 dport = inet_sk(stt_port->stt_sock->sock->sk)->inet_sport; | |
185 | __be16 sport = udp_flow_src_port(net, skb, 1, USHRT_MAX, true); | |
186 | ||
187 | /* Get tp_src and tp_dst, refert to stt_build_header(). | |
188 | */ | |
189 | return ovs_tunnel_get_egress_info(egress_tun_info, | |
190 | ovs_dp_get_net(vport->dp), | |
191 | OVS_CB(skb)->egress_tun_info, | |
192 | IPPROTO_UDP, skb->mark, sport, dport); | |
193 | } | |
194 | ||
195 | static struct vport_ops ovs_stt_vport_ops = { | |
196 | .type = OVS_VPORT_TYPE_STT, | |
197 | .create = stt_tnl_create, | |
198 | .destroy = stt_tnl_destroy, | |
199 | .get_name = stt_tnl_get_name, | |
200 | .get_options = stt_tnl_get_options, | |
201 | .send = stt_tnl_send, | |
202 | .get_egress_tun_info = stt_get_egress_tun_info, | |
203 | .owner = THIS_MODULE, | |
204 | }; | |
205 | ||
206 | static int __init ovs_stt_tnl_init(void) | |
207 | { | |
208 | int err; | |
209 | ||
210 | err = stt_init_module(); | |
211 | if (err) | |
212 | return err; | |
213 | err = ovs_vport_ops_register(&ovs_stt_vport_ops); | |
214 | if (err) | |
215 | stt_cleanup_module(); | |
216 | return err; | |
217 | } | |
218 | ||
219 | static void __exit ovs_stt_tnl_exit(void) | |
220 | { | |
221 | ovs_vport_ops_unregister(&ovs_stt_vport_ops); | |
222 | stt_cleanup_module(); | |
223 | } | |
224 | ||
225 | module_init(ovs_stt_tnl_init); | |
226 | module_exit(ovs_stt_tnl_exit); | |
227 | ||
228 | MODULE_DESCRIPTION("OVS: STT switching port"); | |
229 | MODULE_LICENSE("GPL"); | |
230 | MODULE_ALIAS("vport-type-106"); | |
231 | #endif |