|
@@ -68,6 +68,7 @@ EXPORT_SYMBOL(ip_vs_conn_put);
|
|
|
#ifdef CONFIG_IP_VS_DEBUG
|
|
|
EXPORT_SYMBOL(ip_vs_get_debug_level);
|
|
|
#endif
|
|
|
+EXPORT_SYMBOL(ip_vs_new_conn_out);
|
|
|
|
|
|
static int ip_vs_net_id __read_mostly;
|
|
|
/* netns cnt used for uniqueness */
|
|
@@ -1100,6 +1101,143 @@ static inline bool is_new_conn_expected(const struct ip_vs_conn *cp,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/* Generic function to create new connections for outgoing RS packets
|
|
|
+ *
|
|
|
+ * Pre-requisites for successful connection creation:
|
|
|
+ * 1) Virtual Service is NOT fwmark based:
|
|
|
+ * In fwmark-VS actual vaddr and vport are unknown to IPVS
|
|
|
+ * 2) Real Server and Virtual Service were NOT configured without port:
|
|
|
+ * This is to allow match of different VS to the same RS ip-addr
|
|
|
+ */
|
|
|
+struct ip_vs_conn *ip_vs_new_conn_out(struct ip_vs_service *svc,
|
|
|
+ struct ip_vs_dest *dest,
|
|
|
+ struct sk_buff *skb,
|
|
|
+ const struct ip_vs_iphdr *iph,
|
|
|
+ __be16 dport,
|
|
|
+ __be16 cport)
|
|
|
+{
|
|
|
+ struct ip_vs_conn_param param;
|
|
|
+ struct ip_vs_conn *ct = NULL, *cp = NULL;
|
|
|
+ const union nf_inet_addr *vaddr, *daddr, *caddr;
|
|
|
+ union nf_inet_addr snet;
|
|
|
+ __be16 vport;
|
|
|
+ unsigned int flags;
|
|
|
+
|
|
|
+ EnterFunction(12);
|
|
|
+ vaddr = &svc->addr;
|
|
|
+ vport = svc->port;
|
|
|
+ daddr = &iph->saddr;
|
|
|
+ caddr = &iph->daddr;
|
|
|
+
|
|
|
+ /* check pre-requisites are satisfied */
|
|
|
+ if (svc->fwmark)
|
|
|
+ return NULL;
|
|
|
+ if (!vport || !dport)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ /* for persistent service first create connection template */
|
|
|
+ if (svc->flags & IP_VS_SVC_F_PERSISTENT) {
|
|
|
+ /* apply netmask the same way ingress-side does */
|
|
|
+#ifdef CONFIG_IP_VS_IPV6
|
|
|
+ if (svc->af == AF_INET6)
|
|
|
+ ipv6_addr_prefix(&snet.in6, &caddr->in6,
|
|
|
+ (__force __u32)svc->netmask);
|
|
|
+ else
|
|
|
+#endif
|
|
|
+ snet.ip = caddr->ip & svc->netmask;
|
|
|
+ /* fill params and create template if not existent */
|
|
|
+ if (ip_vs_conn_fill_param_persist(svc, skb, iph->protocol,
|
|
|
+ &snet, 0, vaddr,
|
|
|
+ vport, ¶m) < 0)
|
|
|
+ return NULL;
|
|
|
+ ct = ip_vs_ct_in_get(¶m);
|
|
|
+ if (!ct) {
|
|
|
+ ct = ip_vs_conn_new(¶m, dest->af, daddr, dport,
|
|
|
+ IP_VS_CONN_F_TEMPLATE, dest, 0);
|
|
|
+ if (!ct) {
|
|
|
+ kfree(param.pe_data);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ ct->timeout = svc->timeout;
|
|
|
+ } else {
|
|
|
+ kfree(param.pe_data);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* connection flags */
|
|
|
+ flags = ((svc->flags & IP_VS_SVC_F_ONEPACKET) &&
|
|
|
+ iph->protocol == IPPROTO_UDP) ? IP_VS_CONN_F_ONE_PACKET : 0;
|
|
|
+ /* create connection */
|
|
|
+ ip_vs_conn_fill_param(svc->ipvs, svc->af, iph->protocol,
|
|
|
+ caddr, cport, vaddr, vport, ¶m);
|
|
|
+ cp = ip_vs_conn_new(¶m, dest->af, daddr, dport, flags, dest, 0);
|
|
|
+ if (!cp) {
|
|
|
+ if (ct)
|
|
|
+ ip_vs_conn_put(ct);
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ if (ct) {
|
|
|
+ ip_vs_control_add(cp, ct);
|
|
|
+ ip_vs_conn_put(ct);
|
|
|
+ }
|
|
|
+ ip_vs_conn_stats(cp, svc);
|
|
|
+
|
|
|
+ /* return connection (will be used to handle outgoing packet) */
|
|
|
+ IP_VS_DBG_BUF(6, "New connection RS-initiated:%c c:%s:%u v:%s:%u "
|
|
|
+ "d:%s:%u conn->flags:%X conn->refcnt:%d\n",
|
|
|
+ ip_vs_fwd_tag(cp),
|
|
|
+ IP_VS_DBG_ADDR(cp->af, &cp->caddr), ntohs(cp->cport),
|
|
|
+ IP_VS_DBG_ADDR(cp->af, &cp->vaddr), ntohs(cp->vport),
|
|
|
+ IP_VS_DBG_ADDR(cp->af, &cp->daddr), ntohs(cp->dport),
|
|
|
+ cp->flags, atomic_read(&cp->refcnt));
|
|
|
+ LeaveFunction(12);
|
|
|
+ return cp;
|
|
|
+}
|
|
|
+
|
|
|
+/* Handle outgoing packets which are considered requests initiated by
|
|
|
+ * real servers, so that subsequent responses from external client can be
|
|
|
+ * routed to the right real server.
|
|
|
+ * Used also for outgoing responses in OPS mode.
|
|
|
+ *
|
|
|
+ * Connection management is handled by persistent-engine specific callback.
|
|
|
+ */
|
|
|
+static struct ip_vs_conn *__ip_vs_rs_conn_out(unsigned int hooknum,
|
|
|
+ struct netns_ipvs *ipvs,
|
|
|
+ int af, struct sk_buff *skb,
|
|
|
+ const struct ip_vs_iphdr *iph)
|
|
|
+{
|
|
|
+ struct ip_vs_dest *dest;
|
|
|
+ struct ip_vs_conn *cp = NULL;
|
|
|
+ __be16 _ports[2], *pptr;
|
|
|
+
|
|
|
+ if (hooknum == NF_INET_LOCAL_IN)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ pptr = frag_safe_skb_hp(skb, iph->len,
|
|
|
+ sizeof(_ports), _ports, iph);
|
|
|
+ if (!pptr)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ dest = ip_vs_find_real_service(ipvs, af, iph->protocol,
|
|
|
+ &iph->saddr, pptr[0]);
|
|
|
+ if (dest) {
|
|
|
+ struct ip_vs_service *svc;
|
|
|
+ struct ip_vs_pe *pe;
|
|
|
+
|
|
|
+ svc = rcu_dereference(dest->svc);
|
|
|
+ if (svc) {
|
|
|
+ pe = rcu_dereference(svc->pe);
|
|
|
+ if (pe && pe->conn_out)
|
|
|
+ cp = pe->conn_out(svc, dest, skb, iph,
|
|
|
+ pptr[0], pptr[1]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ rcu_read_unlock();
|
|
|
+
|
|
|
+ return cp;
|
|
|
+}
|
|
|
+
|
|
|
/* Handle response packets: rewrite addresses and send away...
|
|
|
*/
|
|
|
static unsigned int
|
|
@@ -1245,6 +1383,22 @@ ip_vs_out(struct netns_ipvs *ipvs, unsigned int hooknum, struct sk_buff *skb, in
|
|
|
|
|
|
if (likely(cp))
|
|
|
return handle_response(af, skb, pd, cp, &iph, hooknum);
|
|
|
+
|
|
|
+ /* Check for real-server-started requests */
|
|
|
+ if (atomic_read(&ipvs->conn_out_counter)) {
|
|
|
+ /* Currently only for UDP:
|
|
|
+ * connection oriented protocols typically use
|
|
|
+ * ephemeral ports for outgoing connections, so
|
|
|
+ * related incoming responses would not match any VS
|
|
|
+ */
|
|
|
+ if (pp->protocol == IPPROTO_UDP) {
|
|
|
+ cp = __ip_vs_rs_conn_out(hooknum, ipvs, af, skb, &iph);
|
|
|
+ if (likely(cp))
|
|
|
+ return handle_response(af, skb, pd, cp, &iph,
|
|
|
+ hooknum);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
if (sysctl_nat_icmp_send(ipvs) &&
|
|
|
(pp->protocol == IPPROTO_TCP ||
|
|
|
pp->protocol == IPPROTO_UDP ||
|