1 /** @file
2   IP4 option support functions.
3 
4 Copyright (c) 2005 - 2011, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution.  The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9 
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 
13 **/
14 
15 #include "Ip4Impl.h"
16 
17 
18 /**
19   Validate the IP4 option format for both the packets we received
20   and will transmit.
21 
22   @param[in]  Option            The first byte of the option
23   @param[in]  OptionLen         The length of the whole option
24   @param[in]  Rcvd              The option is from the packet we received if TRUE,
25                                 otherwise the option we wants to transmit.
26 
27   @retval TRUE     The option is properly formatted
28   @retval FALSE    The option is mal-formated
29 
30 **/
31 BOOLEAN
Ip4OptionIsValid(IN UINT8 * Option,IN UINT32 OptionLen,IN BOOLEAN Rcvd)32 Ip4OptionIsValid (
33   IN UINT8                  *Option,
34   IN UINT32                 OptionLen,
35   IN BOOLEAN                Rcvd
36   )
37 {
38   UINT32                    Cur;
39   UINT32                    Len;
40   UINT32                    Point;
41 
42   Cur       = 0;
43 
44   while (Cur < OptionLen) {
45     switch (Option[Cur]) {
46     case IP4_OPTION_NOP:
47       Cur++;
48       break;
49 
50     case IP4_OPTION_EOP:
51       Cur = OptionLen;
52       break;
53 
54     case IP4_OPTION_LSRR:
55     case IP4_OPTION_SSRR:
56     case IP4_OPTION_RR:
57       Len   = Option[Cur + 1];
58       Point = Option[Cur + 2];
59 
60       //
61       // SRR/RR options are formatted as |Type|Len|Point|Ip1|Ip2|...
62       //
63       if ((OptionLen - Cur < Len) || (Len < 3) || ((Len - 3) % 4 != 0)) {
64         return FALSE;
65       }
66 
67       if ((Point > Len + 1) || (Point % 4 != 0)) {
68         return FALSE;
69       }
70 
71       //
72       // The Point must point pass the last entry if the packet is received
73       // by us. It must point to 4 if the packet is to be sent by us for
74       // source route option.
75       //
76       if ((Option[Cur] != IP4_OPTION_RR) &&
77           ((Rcvd && (Point != Len + 1)) || (!Rcvd && (Point != 4)))) {
78 
79         return FALSE;
80       }
81 
82       Cur += Len;
83       break;
84 
85     default:
86       Len = Option[Cur + 1];
87 
88       if ((OptionLen - Cur < Len) || (Len < 2)) {
89         return FALSE;
90       }
91 
92       Cur = Cur + Len;
93       break;
94     }
95 
96   }
97 
98   return TRUE;
99 }
100 
101 
102 /**
103   Copy the option from the original option to buffer. It
104   handles the details such as:
105   1. whether copy the single IP4 option to the first/non-first
106      fragments.
107   2. Pad the options copied over to aligned to 4 bytes.
108 
109   @param[in]       Option            The original option to copy from
110   @param[in]       OptionLen         The length of the original option
111   @param[in]       FirstFragment     Whether it is the first fragment
112   @param[in, out]  Buf               The buffer to copy options to. NULL
113   @param[in, out]  BufLen            The length of the buffer
114 
115   @retval EFI_SUCCESS           The options are copied over
116   @retval EFI_BUFFER_TOO_SMALL  Buf is NULL or BufLen provided is too small.
117 
118 **/
119 EFI_STATUS
Ip4CopyOption(IN UINT8 * Option,IN UINT32 OptionLen,IN BOOLEAN FirstFragment,IN OUT UINT8 * Buf,OPTIONAL IN OUT UINT32 * BufLen)120 Ip4CopyOption (
121   IN     UINT8              *Option,
122   IN     UINT32             OptionLen,
123   IN     BOOLEAN            FirstFragment,
124   IN OUT UINT8              *Buf,           OPTIONAL
125   IN OUT UINT32             *BufLen
126   )
127 {
128   UINT8                     OptBuf[40];
129   UINT32                    Cur;
130   UINT32                    Next;
131   UINT8                     Type;
132   UINT32                    Len;
133 
134   ASSERT ((BufLen != NULL) && (OptionLen <= 40));
135 
136   Cur   = 0;
137   Next  = 0;
138 
139   while (Cur < OptionLen) {
140     Type  = Option[Cur];
141     Len   = Option[Cur + 1];
142 
143     if (Type == IP4_OPTION_NOP) {
144       //
145       // Keep the padding, in case that the sender wants to align
146       // the option, say, to 4 bytes
147       //
148       OptBuf[Next] = IP4_OPTION_NOP;
149       Next++;
150       Cur++;
151 
152     } else if (Type == IP4_OPTION_EOP) {
153       //
154       // Don't append the EOP to avoid including only a EOP option
155       //
156       break;
157 
158     } else {
159       //
160       // don't copy options that is only valid for the first fragment
161       //
162       if (FirstFragment || (Type & IP4_OPTION_COPY_MASK) != 0) {
163         CopyMem (OptBuf + Next, Option + Cur, Len);
164         Next += Len;
165       }
166 
167       Cur += Len;
168     }
169   }
170 
171   //
172   // Don't append an EOP only option.
173   //
174   if (Next == 0) {
175     *BufLen = 0;
176     return EFI_SUCCESS;
177   }
178 
179   //
180   // Append an EOP if the end of option doesn't coincide with the
181   // end of the IP header, that is, isn't aligned to 4 bytes..
182   //
183   if ((Next % 4) != 0) {
184     OptBuf[Next] = IP4_OPTION_EOP;
185     Next++;
186   }
187 
188   //
189   // Head length is in the unit of 4 bytes. Now, Len is the
190   // acutal option length to appear in the IP header.
191   //
192   Len = ((Next + 3) &~0x03);
193 
194   //
195   // If the buffer is too small, set the BufLen then return
196   //
197   if ((Buf == NULL) || (*BufLen < Len)) {
198     *BufLen = Len;
199     return EFI_BUFFER_TOO_SMALL;
200   }
201 
202   //
203   // Copy the option to the Buf, zero the buffer first to pad
204   // the options with NOP to align to 4 bytes.
205   //
206   ZeroMem (Buf, Len);
207   CopyMem (Buf, OptBuf, Next);
208   *BufLen = Len;
209   return EFI_SUCCESS;
210 }
211