summaryrefslogtreecommitdiffstats
path: root/NetworkPkg/TcpDxe/TcpIo.c
blob: 5504c30cee838852d4b4e6d2f07b0526082b86a1 (plain)
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/** @file
  Implementation of I/O interfaces between TCP and IpIoLib.

  Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "TcpMain.h"

/**
  Packet receive callback function provided to IP_IO, used to call
  the proper function to handle the packet received by IP.

  @param[in] Status        Result of the receive request.
  @param[in] IcmpErr       Valid when Status is EFI_ICMP_ERROR.
  @param[in] NetSession    The IP session for the received packet.
  @param[in] Pkt           Packet received.
  @param[in] Context       The data provided by the user for the received packet when
                           the callback is registered in IP_IO_OPEN_DATA::RcvdContext.
                           This is an optional parameter that may be NULL.

**/
VOID
EFIAPI
TcpRxCallback (
  IN EFI_STATUS                       Status,
  IN UINT8                            IcmpErr,
  IN EFI_NET_SESSION_DATA             *NetSession,
  IN NET_BUF                          *Pkt,
  IN VOID                             *Context    OPTIONAL
  )
{
  if (EFI_SUCCESS == Status) {
    TcpInput (Pkt, &NetSession->Source, &NetSession->Dest, NetSession->IpVersion);
  } else {
    TcpIcmpInput (
      Pkt,
      IcmpErr,
      &NetSession->Source,
      &NetSession->Dest,
      NetSession->IpVersion
      );
  }
}

/**
  Send the segment to IP via IpIo function.

  @param[in]  Tcb                Pointer to the TCP_CB of this TCP instance.
  @param[in]  Nbuf               Pointer to the TCP segment to be sent.
  @param[in]  Src                Source address of the TCP segment.
  @param[in]  Dest               Destination address of the TCP segment.
  @param[in]  Version            IP_VERSION_4 or IP_VERSION_6

  @retval 0                      The segment was sent out successfully.
  @retval -1                     The segment failed to send.

**/
INTN
TcpSendIpPacket (
  IN TCP_CB          *Tcb,
  IN NET_BUF         *Nbuf,
  IN EFI_IP_ADDRESS  *Src,
  IN EFI_IP_ADDRESS  *Dest,
  IN UINT8           Version
  )
{
  EFI_STATUS       Status;
  IP_IO            *IpIo;
  IP_IO_OVERRIDE   Override;
  SOCKET           *Sock;
  VOID             *IpSender;
  TCP_PROTO_DATA  *TcpProto;

  if (NULL == Tcb) {

    IpIo     = NULL;
    IpSender = IpIoFindSender (&IpIo, Version, Src);

    if (IpSender == NULL) {
      DEBUG ((DEBUG_WARN, "TcpSendIpPacket: No appropriate IpSender.\n"));
      return -1;
    }

    if (Version == IP_VERSION_6) {
      //
      // It's tricky here. EFI IPv6 Spec don't allow an instance overriding the
      // destination address if the dest is already specified through the
      // configuration data. Here we get the IpIo we need and use the default IP
      // instance in this IpIo to send the packet. The dest address is configured
      // to be the unspecified address for the default IP instance.
      //
      IpSender = NULL;
    }
  } else {

    Sock     = Tcb->Sk;
    TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;
    IpIo     = TcpProto->TcpService->IpIo;
    IpSender = Tcb->IpInfo;

    if (Version == IP_VERSION_6) {
      //
      // It's IPv6 and this TCP segment belongs to a solid TCB, in such case
      // the destination address can't be overridden, so reset the Dest to NULL.
      //
      if (!Tcb->RemoteIpZero) {
        Dest = NULL;
      }
    }
  }

  ASSERT (Version == IpIo->IpVersion);

  if (Version == IP_VERSION_4) {
    Override.Ip4OverrideData.TypeOfService = 0;
    Override.Ip4OverrideData.TimeToLive    = 255;
    Override.Ip4OverrideData.DoNotFragment = FALSE;
    Override.Ip4OverrideData.Protocol      = EFI_IP_PROTO_TCP;
    ZeroMem (&Override.Ip4OverrideData.GatewayAddress, sizeof (EFI_IPv4_ADDRESS));
    CopyMem (&Override.Ip4OverrideData.SourceAddress, Src, sizeof (EFI_IPv4_ADDRESS));
  } else {
    Override.Ip6OverrideData.Protocol  = EFI_IP_PROTO_TCP;
    Override.Ip6OverrideData.HopLimit  = 255;
    Override.Ip6OverrideData.FlowLabel = 0;
  }

  Status = IpIoSend (IpIo, Nbuf, IpSender, NULL, NULL, Dest, &Override);

  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "TcpSendIpPacket: return %r error\n", Status));
    return -1;
  }

  return 0;
}

/**
  Refresh the remote peer's Neighbor Cache State if already exists.

  @param[in]  Tcb                Pointer to the TCP_CB of this TCP instance.
  @param[in]  Neighbor           Source address of the TCP segment.
  @param[in]  Timeout            Time in 100-ns units that this entry will remain
                                 in the neighbor cache. A value of zero means that
                                 the entry  is permanent. A value of non-zero means
                                 that the entry is dynamic and will be deleted
                                 after Timeout.

  @retval EFI_SUCCESS            Successfully updated the neighbor relationship.
  @retval EFI_NOT_STARTED        The IpIo is not configured.
  @retval EFI_INVALID_PARAMETER  Any input parameter is invalid.
  @retval EFI_OUT_OF_RESOURCES   Failed to allocate some resources.
  @retval EFI_NOT_FOUND          This entry is not in the neighbor table.

**/
EFI_STATUS
Tcp6RefreshNeighbor (
  IN TCP_CB          *Tcb,
  IN EFI_IP_ADDRESS  *Neighbor,
  IN UINT32          Timeout
  )
{
  IP_IO            *IpIo;
  SOCKET           *Sock;
  TCP_PROTO_DATA  *TcpProto;

  if (NULL == Tcb) {
    IpIo = NULL;
    IpIoFindSender (&IpIo, IP_VERSION_6, Neighbor);

    if (IpIo == NULL) {
      DEBUG ((DEBUG_WARN, "Tcp6AddNeighbor: No appropriate IpIo.\n"));
      return EFI_NOT_STARTED;
    }

  } else {
    Sock     = Tcb->Sk;
    TcpProto = (TCP_PROTO_DATA *) Sock->ProtoReserved;
    IpIo     = TcpProto->TcpService->IpIo;
  }

  return IpIoRefreshNeighbor (IpIo, Neighbor, Timeout);
}