diff options
author | Vlad Yasevich <vyasevich@gmail.com> | 2015-01-31 10:40:15 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-02-02 22:28:04 -0500 |
commit | 6422398c2ab09268a55112f98cbf96bbf0184328 (patch) | |
tree | 1a3bdd60945346bbdbe6e017807d6a8c78889110 /net/ipv6 | |
parent | 0bbe84a67b0b5460c2e67101b3aa95cb828845f3 (diff) |
ipv6: introduce ipv6_make_skb
This commit is very similar to
commit 1c32c5ad6fac8cee1a77449f5abf211e911ff830
Author: Herbert Xu <herbert@gondor.apana.org.au>
Date: Tue Mar 1 02:36:47 2011 +0000
inet: Add ip_make_skb and ip_finish_skb
It adds IPv6 version of the helpers ip6_make_skb and ip6_finish_skb.
The job of ip6_make_skb is to collect messages into an ipv6 packet
and poplulate ipv6 eader. The job of ip6_finish_skb is to transmit
the generated skb. Together they replicated the job of
ip6_push_pending_frames() while also provide the capability to be
called independently. This will be needed to add lockless UDP sendmsg
support.
Signed-off-by: Vladislav Yasevich <vyasevic@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r-- | net/ipv6/ip6_output.c | 103 |
1 files changed, 84 insertions, 19 deletions
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 1b66453a695d..b89d3c27dac7 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c | |||
@@ -1564,22 +1564,23 @@ static void ip6_cork_release(struct inet_cork_full *cork, | |||
1564 | memset(&cork->fl, 0, sizeof(cork->fl)); | 1564 | memset(&cork->fl, 0, sizeof(cork->fl)); |
1565 | } | 1565 | } |
1566 | 1566 | ||
1567 | int ip6_push_pending_frames(struct sock *sk) | 1567 | struct sk_buff *__ip6_make_skb(struct sock *sk, |
1568 | struct sk_buff_head *queue, | ||
1569 | struct inet_cork_full *cork, | ||
1570 | struct inet6_cork *v6_cork) | ||
1568 | { | 1571 | { |
1569 | struct sk_buff *skb, *tmp_skb; | 1572 | struct sk_buff *skb, *tmp_skb; |
1570 | struct sk_buff **tail_skb; | 1573 | struct sk_buff **tail_skb; |
1571 | struct in6_addr final_dst_buf, *final_dst = &final_dst_buf; | 1574 | struct in6_addr final_dst_buf, *final_dst = &final_dst_buf; |
1572 | struct inet_sock *inet = inet_sk(sk); | ||
1573 | struct ipv6_pinfo *np = inet6_sk(sk); | 1575 | struct ipv6_pinfo *np = inet6_sk(sk); |
1574 | struct net *net = sock_net(sk); | 1576 | struct net *net = sock_net(sk); |
1575 | struct ipv6hdr *hdr; | 1577 | struct ipv6hdr *hdr; |
1576 | struct ipv6_txoptions *opt = np->cork.opt; | 1578 | struct ipv6_txoptions *opt = v6_cork->opt; |
1577 | struct rt6_info *rt = (struct rt6_info *)inet->cork.base.dst; | 1579 | struct rt6_info *rt = (struct rt6_info *)cork->base.dst; |
1578 | struct flowi6 *fl6 = &inet->cork.fl.u.ip6; | 1580 | struct flowi6 *fl6 = &cork->fl.u.ip6; |
1579 | unsigned char proto = fl6->flowi6_proto; | 1581 | unsigned char proto = fl6->flowi6_proto; |
1580 | int err = 0; | ||
1581 | 1582 | ||
1582 | skb = __skb_dequeue(&sk->sk_write_queue); | 1583 | skb = __skb_dequeue(queue); |
1583 | if (skb == NULL) | 1584 | if (skb == NULL) |
1584 | goto out; | 1585 | goto out; |
1585 | tail_skb = &(skb_shinfo(skb)->frag_list); | 1586 | tail_skb = &(skb_shinfo(skb)->frag_list); |
@@ -1587,7 +1588,7 @@ int ip6_push_pending_frames(struct sock *sk) | |||
1587 | /* move skb->data to ip header from ext header */ | 1588 | /* move skb->data to ip header from ext header */ |
1588 | if (skb->data < skb_network_header(skb)) | 1589 | if (skb->data < skb_network_header(skb)) |
1589 | __skb_pull(skb, skb_network_offset(skb)); | 1590 | __skb_pull(skb, skb_network_offset(skb)); |
1590 | while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) { | 1591 | while ((tmp_skb = __skb_dequeue(queue)) != NULL) { |
1591 | __skb_pull(tmp_skb, skb_network_header_len(skb)); | 1592 | __skb_pull(tmp_skb, skb_network_header_len(skb)); |
1592 | *tail_skb = tmp_skb; | 1593 | *tail_skb = tmp_skb; |
1593 | tail_skb = &(tmp_skb->next); | 1594 | tail_skb = &(tmp_skb->next); |
@@ -1612,10 +1613,10 @@ int ip6_push_pending_frames(struct sock *sk) | |||
1612 | skb_reset_network_header(skb); | 1613 | skb_reset_network_header(skb); |
1613 | hdr = ipv6_hdr(skb); | 1614 | hdr = ipv6_hdr(skb); |
1614 | 1615 | ||
1615 | ip6_flow_hdr(hdr, np->cork.tclass, | 1616 | ip6_flow_hdr(hdr, v6_cork->tclass, |
1616 | ip6_make_flowlabel(net, skb, fl6->flowlabel, | 1617 | ip6_make_flowlabel(net, skb, fl6->flowlabel, |
1617 | np->autoflowlabel)); | 1618 | np->autoflowlabel)); |
1618 | hdr->hop_limit = np->cork.hop_limit; | 1619 | hdr->hop_limit = v6_cork->hop_limit; |
1619 | hdr->nexthdr = proto; | 1620 | hdr->nexthdr = proto; |
1620 | hdr->saddr = fl6->saddr; | 1621 | hdr->saddr = fl6->saddr; |
1621 | hdr->daddr = *final_dst; | 1622 | hdr->daddr = *final_dst; |
@@ -1632,25 +1633,45 @@ int ip6_push_pending_frames(struct sock *sk) | |||
1632 | ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); | 1633 | ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTMSGS); |
1633 | } | 1634 | } |
1634 | 1635 | ||
1636 | ip6_cork_release(cork, v6_cork); | ||
1637 | out: | ||
1638 | return skb; | ||
1639 | } | ||
1640 | |||
1641 | int ip6_send_skb(struct sk_buff *skb) | ||
1642 | { | ||
1643 | struct net *net = sock_net(skb->sk); | ||
1644 | struct rt6_info *rt = (struct rt6_info *)skb_dst(skb); | ||
1645 | int err; | ||
1646 | |||
1635 | err = ip6_local_out(skb); | 1647 | err = ip6_local_out(skb); |
1636 | if (err) { | 1648 | if (err) { |
1637 | if (err > 0) | 1649 | if (err > 0) |
1638 | err = net_xmit_errno(err); | 1650 | err = net_xmit_errno(err); |
1639 | if (err) | 1651 | if (err) |
1640 | goto error; | 1652 | IP6_INC_STATS(net, rt->rt6i_idev, |
1653 | IPSTATS_MIB_OUTDISCARDS); | ||
1641 | } | 1654 | } |
1642 | 1655 | ||
1643 | out: | ||
1644 | ip6_cork_release(&inet->cork, &np->cork); | ||
1645 | return err; | 1656 | return err; |
1646 | error: | 1657 | } |
1647 | IP6_INC_STATS(net, rt->rt6i_idev, IPSTATS_MIB_OUTDISCARDS); | 1658 | |
1648 | goto out; | 1659 | int ip6_push_pending_frames(struct sock *sk) |
1660 | { | ||
1661 | struct sk_buff *skb; | ||
1662 | |||
1663 | skb = ip6_finish_skb(sk); | ||
1664 | if (!skb) | ||
1665 | return 0; | ||
1666 | |||
1667 | return ip6_send_skb(skb); | ||
1649 | } | 1668 | } |
1650 | EXPORT_SYMBOL_GPL(ip6_push_pending_frames); | 1669 | EXPORT_SYMBOL_GPL(ip6_push_pending_frames); |
1651 | 1670 | ||
1652 | static void __ip6_flush_pending_frames(struct sock *sk, | 1671 | static void __ip6_flush_pending_frames(struct sock *sk, |
1653 | struct sk_buff_head *queue) | 1672 | struct sk_buff_head *queue, |
1673 | struct inet_cork_full *cork, | ||
1674 | struct inet6_cork *v6_cork) | ||
1654 | { | 1675 | { |
1655 | struct sk_buff *skb; | 1676 | struct sk_buff *skb; |
1656 | 1677 | ||
@@ -1661,11 +1682,55 @@ static void __ip6_flush_pending_frames(struct sock *sk, | |||
1661 | kfree_skb(skb); | 1682 | kfree_skb(skb); |
1662 | } | 1683 | } |
1663 | 1684 | ||
1664 | ip6_cork_release(&inet_sk(sk)->cork, &inet6_sk(sk)->cork); | 1685 | ip6_cork_release(cork, v6_cork); |
1665 | } | 1686 | } |
1666 | 1687 | ||
1667 | void ip6_flush_pending_frames(struct sock *sk) | 1688 | void ip6_flush_pending_frames(struct sock *sk) |
1668 | { | 1689 | { |
1669 | __ip6_flush_pending_frames(sk, &sk->sk_write_queue); | 1690 | __ip6_flush_pending_frames(sk, &sk->sk_write_queue, |
1691 | &inet_sk(sk)->cork, &inet6_sk(sk)->cork); | ||
1670 | } | 1692 | } |
1671 | EXPORT_SYMBOL_GPL(ip6_flush_pending_frames); | 1693 | EXPORT_SYMBOL_GPL(ip6_flush_pending_frames); |
1694 | |||
1695 | struct sk_buff *ip6_make_skb(struct sock *sk, | ||
1696 | int getfrag(void *from, char *to, int offset, | ||
1697 | int len, int odd, struct sk_buff *skb), | ||
1698 | void *from, int length, int transhdrlen, | ||
1699 | int hlimit, int tclass, | ||
1700 | struct ipv6_txoptions *opt, struct flowi6 *fl6, | ||
1701 | struct rt6_info *rt, unsigned int flags, | ||
1702 | int dontfrag) | ||
1703 | { | ||
1704 | struct inet_cork_full cork; | ||
1705 | struct inet6_cork v6_cork; | ||
1706 | struct sk_buff_head queue; | ||
1707 | int exthdrlen = (opt ? opt->opt_flen : 0); | ||
1708 | int err; | ||
1709 | |||
1710 | if (flags & MSG_PROBE) | ||
1711 | return NULL; | ||
1712 | |||
1713 | __skb_queue_head_init(&queue); | ||
1714 | |||
1715 | cork.base.flags = 0; | ||
1716 | cork.base.addr = 0; | ||
1717 | cork.base.opt = NULL; | ||
1718 | v6_cork.opt = NULL; | ||
1719 | err = ip6_setup_cork(sk, &cork, &v6_cork, hlimit, tclass, opt, rt, fl6); | ||
1720 | if (err) | ||
1721 | return ERR_PTR(err); | ||
1722 | |||
1723 | if (dontfrag < 0) | ||
1724 | dontfrag = inet6_sk(sk)->dontfrag; | ||
1725 | |||
1726 | err = __ip6_append_data(sk, fl6, &queue, &cork.base, &v6_cork, | ||
1727 | ¤t->task_frag, getfrag, from, | ||
1728 | length + exthdrlen, transhdrlen + exthdrlen, | ||
1729 | flags, dontfrag); | ||
1730 | if (err) { | ||
1731 | __ip6_flush_pending_frames(sk, &queue, &cork, &v6_cork); | ||
1732 | return ERR_PTR(err); | ||
1733 | } | ||
1734 | |||
1735 | return __ip6_make_skb(sk, &queue, &cork, &v6_cork); | ||
1736 | } | ||