Product Overview » cFosSpeed Filter Expressions

cFosSpeed Filter Expressions

Overview

cFosSpeed supports a number of different traffic classes set to different priorities. In effect, this means packets in classes with a higher priority are sent out prior to packets from classes with lower priority - even if low-priority packets arrived before high-priority packets in their respective class queues.

That way, cFosSpeed can prioritize specific packets permitting them to take the "fast lane" and zip past "normal" traffic. This priority system can be modified and expanded by the user with what we call "filter expressions." These can be used to filter out packets and assign them to a desired traffic class.

Filter expressions consist of a series of "filter rules" that all need to be true for the entire expression to be true. If the entire expression is true, the packet is put into the corresponding class queue.

A list of filter expressions does allow you to filter for several different packets. This list is checked from top to bottom; the first filter expression found to be true (i.e., consisting only of true filter rules) is employed, ending the list traversal.

Packets not matching any particular filter expression are automatically assigned to the "default" class, which is a built-in class with priority 0.

A silly and simple example

A sample list of filter expressions is given below (the output looks like the "spd filter" output described later in this overview). Note that this sample is provided for illustration purposes only; it has no other use.

     0  -p icmp -c fast
     1  -tcp-sport 1234 -tcp-dport 5678 -c special

This would mean that if a packet is of IP protocol "icmp", it would be assigned to "fast" class, ending traversal of the list.

If a packet does not match the first expression, then it will be checked for the second expression. In this case, the latter consists of two rules, which must both be true for the entire expression to be true (a boolean AND).

The "-tcp-sport" entry checks the TCP source port number. Accordingly, for this rule to be true, the packet must have IP protocol TCP and the TCP source port must be 1234.

Then, there is a second rule, which is true if (again) it's a TCP packet and the TCP destination port is 5678.

Thus, all TCP traffic transmitted from port 1234 to port 5678 would be assigned to "special" class (which doesn't exist by default).

If neither of the two rules apply to the packet (which should happen fairly often), it is put into "default" class instead.

Filter rules reference

Now let's go into more detail.

Each rule can be negated by preceding it with a '!' (exclamation mark) or by putting the '!' in between the option name and value (if such a value exists). So it has to be "!-f", but you could also use "!-p icmp" or "-p !icmp".

Underlying most rules are one or more implicit assumptions; for instance, -tcp-length assumes it's a TCP packet after all. These assumptions are listed with each rule. If a packet does not match the assumptions, the rule is false. Note that a rule with a non-matching implicit assumtion is false even if it is negated (by using '!').

That is, a !-tcp-length 0 rule matches TCP packets of any length other than zero, but does not match any non-TCP packet.

If you need to specify a number, you can enter the number either in decimal (just type it in as is) or hexadecimal (prefix it with "0x") format. For example, 16 and 0x10 both denote decimal number 16.

For some rules, a range of values can be entered. Allowable value ranges are:

num
like "99".
A single number that must be matched exactly. As usual, the number can be entered either in decimal or hex format.
from:to
like "42:99".
Range of integers starting and including "from", while also including and ending with "to". If "from" is higher than "to", the two values are exchanged.
:to
like ":99".
Short for "0:to"; in this example, short for "0:99".
from:
like "42:".
Short for "from:0xffffffff".

What's more, a list of ranges can be entered for some rules. That is simply a concatenation of several ranges, separated by commas (,). For example, "1:2,3:4" is just another way of writing "1:4". There must be no space around the comma.

Basic rules

-tx
Transmit. True if the packet is sent. There is no implied assuption with this rule.

We maintain separate lists of filters for both transmit and receive. By using this rule you specify that a filter is only to be put on the transmit list. Since this can't change in session, the rule is not re-evaluated each time a packet comes in; we call it a "cheap rule".
-rx
Receive. True if the packet is received. There is no implied assuption with this rule.

Comments of "-tx" rule apply analogously.
-wan
Packet is transmitted on a WAN port (i.e., on NDISWANIP device) in contrast to LAN port. Is always true for cFos. There is no implied assuption with this rule. This rule is "cheap", too.
-bridged
Packet is transmitted on a LAN port and your modem works as a bridge, not as a router.
That is, the IP subnet is not your own, but your provider's. There is no implied assuption with this rule. This rule is "cheap" only on a WAN port, since this is always considered bridged.
-eth
The port operates with Ethernet framing.

This is always true for cFosSpeed and always false for cFos. This rule is "cheap" and has no implied assumption.
-ethp protocols
Ethernet protocol number.

This rule has the implied assumption that Ethernet framing is used, i.e., it is always false for cFos.

You can specify a list of ranges for "protocols". Protocol numbers may be written in decimal or hex.

Some protocol numbers are:
0x800
IP, Internet Protocol version 4
0x806
ARP, Address Resolution Protocol
0x86dd
IP, Internet Protocol version 6
For a more extensive list of protocol numbers see www.iana.org/assignments/ethernet-numbers .
-ppp
The port operates with PPP framing.

This is always true for cFos and always false for cFosSpeed. This rule is "cheap" and has no implied assumption.

Details on PPP can be found in RFC 1661 (The Point-to-Point Protocol), www.ietf.org/rfc/rfc1661.txt .
-pppp protocols
PPP protocol number.

This rule has the implied assumption that PPP framing is used, i.e., it is always false for cFosSpeed.

You can specify a list of ranges for "protocols". Protocol numbers may be written in decimal or hex.

Some protocol numbers are:
0x21
IP, Internet Protocol version 4
0x2d
Van Jacobson Compressed TCP/IP
0x2f
Van Jacobson Uncompressed TCP/IP
0x57
IPv6, Internet Protocol version 6
0x8021
Internet Protocol Control Protocol
0x8057
IPv6 Control Protocol
0xc021
Link Control Protocol
For a bigger list of protocol numbers see www.iana.org/assignments/ppp-numbers .
-ipv4
IP version 4. True if the packet is IPv4 and basically well-formed.
-ipv6
IP version 6. True if the packet is IPv6 and basically well-formed.
-mp-header
The packet has a Multilink-PPP header.

This rule has the implied assumption that PPP framing is used, i.e., it is always false for cFosSpeed.

Details on PPP Multilink can be found in RFC 1990 (The PPP Multilink Protocol), www.ietf.org/rfc/rfc1990.txt .
-mp-begin
The packet has the (B)eginning fragment bit set in the Multilink header.

This rule has the implied assumption that the packet has a Multilink PPP header, i.e., the rule is always false for cFosSpeed.
-mp-end
The packet has the (E)nding fragment bit set in the Multilink header.

This rule has the implied assumption that the packet has a Multilink PPP header, i.e., the rule is always false for cFosSpeed.
-mp-frag
The packet has in its Multilink header either no (B)eginning fragment bit set, or (E)nding fragment bit set, or both.

This rule has the implied assumption that the packet has a Multilink PPP header, i.e., the rule is always false for cFosSpeed.
-rand prob chance
Pseudo-random number.

The rule returns true if a pseudo-random number between 1 and "prob" (borders included) is <= "chance". "prob" must be between 1 and 2^32-1, "chance" can even be 0.

The pseudo-random numbers are generated by a comparatively poor generator (LCM style). It will still look "random" enough for packet-loss tests.

Example: This rule will be true in 5% of all cases (statistically):
-rand 100 5

IP header filter rules

Details on the IP v4 header can be found in RFC 791 (Internet Protocol), www.ietf.org/rfc/rfc0791.txt and details on the IPv6 header can be found in RFC 2460 (Internet Protocol, Version 6) www.ietf.org/rfc/rfc2460.txt .

IP header filter rules apply only to IP packets with a correct header and checksum (IPv4 only, IPv6 has no header checksum). These rules work on any IP packet, fragment or not.

-s address-list
Source IP address.

There are several ways to specify "address-list":

adr
like 1.2.3.4
or 2001:0db8:0000:0000:0000:ff00:0042:8329
or [2001:db8::ff00:42:8329]
This rule matches exactly one address with no wildcarding possible.
adr/netmask
like 1.2.3.0/255.255.255.0
This can be used to specify an "IP subnet". The match works as follows: If the address that is to be tested AND the "netmask" are equal to "adr", then there is a match, otherwise not. This works just like IP routing rules.
adr/bits
like 1.2.3.0/24
or 2001:0db8::/64
This is an alternative form of the adr/netmask approach above. Here, "bits" refer to the number of "1" bits, starting at the left of a 32-bit (IPv4) resp. 128-bit (IPv6) number. Thus, for IPv4 "/24" is 24 1-bits and eight 0-bits. In the regular format separated by dots, this would come out as 255.255.255.0 (since 255 is 11111111 in binary code).
adr,adr...
like 1.2.3.0/24,4.5.6.7,8.9.0.0/255.255.0.0
or 2001:0db8::/64,[fe80::1],8.9.0.0/16
You can make a list of any of the above used notations and mix IPv4 and IPv6. Don't leave any spaces and separate the items with a comma (',').
-d address-list
Destination IP address, where "address-list" has the same format as explained for the -s filter.
-l address-list
Local IP address, where "address-list" has the same format as explained for the -s filter.
The local address is the source address of a sent packet and the destination address of a received packet.
-r address-list
Remote IP address, where "address-list" has the same format as explained for the -s filter.
The remote address is the destination address of a sent packet and the source address of a received packet.
-p protocol
IP protocol, where "protocol" is either a decimal or hex number or one of the following hard-coded names:
ICMP
protocol number 1
HOPOPT
protocol number 0
IGMP
protocol number 2
TCP
protocol number 6
UDP
protocol number 17
IPV6
protocol number 41
IPV6-ROUTE
protocol number 43
IPV6-FRAG
protocol number 44
GRE
protocol number 47
ESP
protocol number 50
AH
protocol number 51
ICMPv6
protocol number 58
IPV6-NONXT
protocol number 59
IPV6-OPTS
protocol number 60
MOBILITY
protocol number 135
-f
IP datagram is not the first fragment (that is, the IP header field "fragmentation offset" is not equal to 0).
-mf
"More Fragments" bit in IP header set.
-fragment
Datagram is not a complete IP frame, but a fragment. That is, either "fragmentation offset" is not 0 or the "More Fragments" bit is set.
-df
"Don't Fragment" bit in IP header set (IPv4 only, there is no such bit under IPv6).
-tos type-of-service
Type of service.

"type-of-service" can be any decimal or hex number between 0 and 255. Note that TOS is neither used nor supported much any more. For details, see RFC 1349 (Type of Service, www.ietf.org/rfc/rfc1349.txt ). See -dscp option below for a modern way of interpreting the TOS field. www.iana.org/assignments/ip-parameters lists common TOS values. Under IPv6 this rule refers to the "traffic class" field of the header.
-dscp dscp-value
Differentialed Services Coding Point, an alternate way of interpreting the TOS field.

"dscp-value" can be a value between 0 and 63 or a range of such values. For details, see RFC 2474 (Differentiated Services, www.ietf.org/rfc/rfc2474.txt ). See -tos option above for the traditional (now almost extinct) way of interpreting the TOS field.
-ttl ttl-value
IP header field "Time to live".

"ttl-value" can be a range of numbers between 0 and 255. See "range" below on how to specify a range.
-length value
Length of the IP datagram or fragment, including the header.

You can specify a range for "value", which is the length of the packet about to be send. With a fragment, it's not the length of the reassembled datagram but the length of the individual fragment.
-data-length value
Length of the data portion of an IP datagram, which is the length of the (reassembled) datagram minus the IP header length.

For IPv6, this does not include the HOPOPT, ROUTE, DESTOPTS, MOBILITY and FRAG extension headers. They are not considered part of the "data".
You can also specify a range for "value".
-ihl value
Length of the IP v4 header, with values anywhere from 20 to 60 (IPv4 only). Again, you can specify a range for "value".
-s-mynet
The source address is in your subnet.
-d-mynet
The destination address is in your subnet.
-l-mynet
The local address is in your subnet.
-r-mynet
The remote address is in your subnet.
-s-bcast
The source address is the subnet directed broadcast address (IPv4 only, there is no such concept under IPv6). I.e., if your subnet is 192.168.1.0 and has subnet mask 255.255.255.0, this rule will match on source address 192.168.1.255.

Note that this filter will not match the limited broadcast address 255.255.255.255.
-d-bcast
The destination address is the subnet directed broadcast address. See -s-bcast for more info.
-l-bcast
The local address is the subnet directed broadcast address. See -s-bcast for more info.
-r-bcast
The remote address is the subnet directed broadcast address. See -s-bcast for more info.
-s-iplist list
The source address is listed in the iplist named 'list'. For more on iplist's see "IP-range lists".
-d-iplist list
The destination address is listed in the iplist named 'list'. For more on iplist's see "IP-range lists".
-l-iplist list
The local address is listed in the iplist named 'list'. For more on iplist's see "IP-range lists".
-r-iplist list
The remote address is listed in the iplist named 'list'. For more on iplist's see "IP-range lists".
-ip-opt list
Packet has at least one of the listed IP-options in its header (IPv4 only). The options can be set in decimal or hex and are separated with a comma, like "131,137". A list of IP-options is here: www.iana.org/assignments/ip-parameters .

UDP header filter rules

Details on the UDP header can be found in RFC 768 (User Datagram Protocol, www.ietf.org/rfc/rfc0768.txt ). Note that each UDP header is prefixed by an IP (v4 or v6) header, since UDP needs IP for transmission.

UDP header filter rules apply only to packets with a correct IP header, protocol set to UDP, and correct UDP header checksum. In addition, they must either be unfragmented or the last fragment that completes the reassembled datagram.

-udp-sport ports
UDP source-port number, or a range of several such ports, or a list of ranges (see above on how to specify them).

A UDP connection is identified by the following four values: source IP address, source UDP port number, destination IP address, and destination UDP port number. So, whenever you send a UDP packet from A to B, the packet always contains those four values.

Since these filter rules are only used for outgoing data packets, "source port" always refers to the port number of the connection on your (local) side.
-udp-dport ports
UDP destination-port number, or a range of several such ports, or a list of ranges (see above on how to specify them).

See -udp-sport for more on port numbers.

Since these filter rules are only used for outgoing data packets, "destination port" always refers to the port number of the connection on the remote side.
-udp-lport ports
Local UDP port number, or a range of several such ports, or a list of ranges (see above on how to specify them).

See -udp-sport for more on port numbers.
-udp-lport ports
Remote UDP port number, or a range of several such ports, or a list of ranges (see above on how to specify them).

See -udp-sport for more on port numbers.
-udp-length value
Length of the UDP data portion including the UDP header as mentioned in the UDP header.

Since the UDP header is as well counted in "value", even the smallest UDP packet possible would have a length of 8. Note that this is different from the concept of -tcp-length (see below).
-udp-prog programnames
Name of the program that sent the datagram.

Note: This only works starting with Windows XP and it only works if the program is running on the same machine as cFosSpeed is (that is, it does not work if cFosSpeed is used on a router and the program is running on a client).

"programnames" can be:
program name
Must be a full file name with extension, but no path (like ftp.exe). Case does not matter. If the program name includes a space, put it in quotes (like "the best.exe").
@section name
The name of a section in settings.ini that contains names of programs (like @lowprogs with a [lowprogs] section somewhere is settings.ini).
@file/section name
The name of a section in "file". "file" can be a fully specified pathname or "user.ini" to specify user.ini in cFosSpeed directory. The handling is otherwise like that of "@section" above.
list
A comma delimited form of the above. There must be no spaces before or after the commas. Example: ftp.exe,telnet.exe,"the best.exe",@lowprogs

See "Managing program names" later on how to manage keys in sections.

TCP header filter rules

Details on the TCP header can be found in RFC 793 (Transmission Control Protocol, www.ietf.org/rfc/rfc0793.txt ). Note that each TCP header is prefixed by an IP (v4 or v6) header, since TCP needs IP for transmission.

TCP header filter rules apply only to packets with a correct IP header, and protocol set to TCP. They must be either unfragmented or the last fragment that completes the reassembled datagram.

-tcp-sport ports
TCP source port number, or a range of them, or a list of ranges (see above on how to specify them).

A TCP connection is identified by the following four values: source IP address, source TCP port number, destination IP address, and destination TCP port number. So, whenever you send a TCP packet from A to B, the packet always contains those four values.

Since these filter rules are only used for outgoing data packets, "source port" always refers to the port number of the connection on your (local) side.
-tcp-dport ports
TCP destination port number, or a range of several such ports, or a list of ranges (see above on how to specify them).

See -tcp-sport for more on port numbers.

Since these filter rules are only used for outgoing data packets, "destination port" always refers to the port number of the connection on the remote side.
-tcp-lport ports
Local TCP port number, or a range of several such ports, or a list of ranges (see above on how to specify them).

See -tcp-sport for more on port numbers.
-tcp-rport ports
Remote TCP port number, or a range of several such ports, or a list of ranges (see above on how to specify them).

See -tcp-sport for more on port numbers.
-tcp-length value
Length of the TCP data portion, also know as the "TCP segment."

In contrast to the concept of -udp-length or -icmp-length, this does not include TCP header length, meaning that the smallest TCP packet possible has a -tcp-length of 0.
-thl value
TCP header length, with values anywhere from 20 to 60. You can also specify a range for "value".
-tcp-flags mask result
Check for the presence of certain flags in the TCP header.

The value of the TCP flags in the header are ANDed with "mask" and the packet is matched if that equals "result". Both "mask" and "result" must be specified as a list of flags. Recognizable flags are: FIN, SYN, RST, PSH, ACK, and URG. ALL stands for all six of them and NONE for none.

Examples:
-tcp-flags SYN SYN
means: if the SYN bit is set (regardless of the other bits), then the rule matches.
-tcp-flags ALL SYN
means: if only the SYN bit is set and all others are zero, then the rule is a match.
-tcp-flags SYN,ACK ACK
means: if SYN is not set and ACK is set, we have a match.
-tcp-flags URG,PSH NONE
means: if neither URG nor PSH are set, it's a match.
-syn
Synonymous for "-tcp-flags SYN,RST,ACK SYN", meaning it only matches the first packet in the TCP three-way-handshake.
-tcp-prog programnames
Name of the program that sent the segment.

Note: This only works starting with Windows XP and it only works if the program is running on the same machine as cFosSpeed is (that is, it does not work if cFosSpeed is used on a router and the program is running on a client).

To learn how to specify "programnames" see -udp-prog rule.
-tcp-connections range
Number of total TCP connections is in "range". Half-open connections are counted as well. The number of connections doesn't need to exactly match either side's count, since cFosSpeed uses its own TCP connection tracking.
-tcp-opt list
Packet has at least one of the listed options in its TCP header. The comments of "-ip-opt" apply. A list of TCP-options is here: www.iana.org/assignments/tcp-parameters .
-tcp-session value
Packet is part of a TCP connection that is in state "value". Possible states are:

0
A TCB has been created
1
At least one side has sent a SYN
2
Both sides have sent SYNs
3
Both sides have sent SYNs that have been ACKed: connection established
4
At least one side has sent a FIN
5
Both sides have sent FINs
6
Both sides have sent FINs that have been ACKed: connection closed
7
At least one side has sent a RST
8
At least one side has sent a FIN that has been ACKed: connection half-closed
-tcp-session-dadr value
There is a TCP connection in state "value" between the same source and destination address as this packet.

Possible states are the same as listed under "-tcp-session".

This rule only implies that the packet is a valid IP packet, but not necessarily TCP.
-tcp-initiated direction
Check which side initiated the TCP connection.

"tx" means that the first SYN was sent from local, "rx" that the first SYN was received from the net.
-tcp-seq range
TCP sequence number (subtracted by the initial sequence number) on the particular TCP connection.

TCP keeps your sent data in sequence. So even if packets get lost, corrupted or out of order on the way from source to destination host, TCP will get it right for you (yeah!). To achieve this, TCP packets have a sequence number, so TCP knows the correct order of them. This sequence number is increased with every byte that is transmitted on the connection.

The filter rule lets you use the sequence number as a means of how many bytes have been transmitted on the connection.

"range" can be a range of numbers.

I.e. "-tcp-seq :500" will only match TCP packets that cover the first 501 bytes in their connection (0 to 500, inclusive).
-tcp-cnt range
TCP packet counter for this particular connection.

This counter is increased by one with each packet sent.

UDP and TCP combo rules

Sometimes you have to specify the same rules for UDP and TCP ports. This can be done by the following combo rules.

The assumptions for UDP and TCP packets each still apply.

-sport ports
If it's a UDP packet, synonymous for "-udp-sport ports"; if it's a TCP packet, synonymous for "-tcp-sport ports". Otherwise, there is no match.
-dport ports
If it's a UDP packet, synonymous for "-udp-dport ports"; if it's a TCP packet, synonymous for "-tcp-dport ports". Otherwise, there is no match.
-lport ports
If it's a UDP packet, synonymous for "-udp-lport ports"; if it's a TCP packet, synonymous for "-tcp-lport ports". Otherwise, there is no match.
-rport ports
If it's a UDP packet, synonymous for "-udp-rport ports"; if it's a TCP packet, synonymous for "-tcp-rport ports". Otherwise, there is no match.
-prog programnames
Name of the program that sent the segment.

Note: This only works starting with Windows XP AND it only works if the program is running on the same machine as cFosSpeed is (that is, it does not work if cFosSpeed is used on a router and the program is running on a client).

To learn how to specify "programnames" see -udp-prog rule.
-l7-prot protnames
Check if packet belongs to a TCP connection resp. UDP pseudo-connection with a certain layer-7 protocol. A list of supported layer-7 protocols is in settings.ini, sections highprots, normalprots, lowprots and lowestprots.

For certain protocols (like IRC, Telnet, file sharing, etc.) there is a further check built in: if the rule is about to match and the class the packet will be queued into is higher than default, then the connection must not be in "bulk mode". cFosSpeed uses statistics to estimate if the connection is is bulk mode, i.e. if data is being constantly sent in large amounts. If the connection is assumed to be in bulk mode, then the -l7-prot rule does not match. This is to prevent protocols that can be used for both bulk and interactive transfer to clog the high classes if the protocol is in bulk mode. This auto-bulk-detection can be switched off by typing the command "spd gset bulk_detect 0".

This rule has the implied assumption that layer-7 detection is on, i.e. that global variable l7_detect is 1.

ICMP filter rules

Details on the ICMP for IPv4 header can be found in RFC 792 (Internet Control Message Protocol, www.ietf.org/rfc/rfc0792.txt ) and details on the ICMPv6 header can be found in RFC 4443 (Internet Control Message Protocol (ICMPv6) for IPv6, www.ietf.org/rfc/rfc4443.txt ). Note that each ICMP header is prefixed by an IP (v4 or v6) header, since ICMP needs IP for transmission.

ICMP header filter rules apply only to packets with a correct IP header, protocol set to ICMP or ICMPv6, and correct ICMP checksum. Such packets must also be either unfragmented or the last fragment that completes the unfragmented datagram.

-icmp-type type - or - -icmp-type type/code
Check for ICMP v4 type and code (if specified).

Both "type" and "code" can be any number between 0 and 255. If you specify no "/" and no code, it will match only on the "type" value. Otherwise, the "code" value must match as well.

You can specify a list of type or type/code as well. Use commas to separate the entries in the list.
-icmpv6-type type - or - -icmpv6-type type/code
Check for ICMPv6 type and code (if specified).

The syntax and semantics for this rule are like -icmp-type.
-icmp-length value
Length of the ICMP data portion, including the ICMP header. This is the IP v4 or v6 datagram length minus the IP header length.

Again, "value" can also be a range of values.

VLAN rules

Details on VLAN can be found in IEEE 802.1Q (e.g. on Wikipedia ).

The VLAN rules (except -is-vlan) only apply if the packet has a VLAN header at all.
-is-vlan
Matches if a packet has a VLAN header.
-vlan-prio range
VLAN packets have a 3 bit priority field (i.e. the priority is 0-7). If this VLAN priority is in "range", this rules matches.
-vlan-id range
VLAN packets also have a 12 bit ID field (i.e. 0-0xfff or 0-4095). If the ID is in "range", this rule matches.

Standard rules

cFosSpeed ships with five traffic classes, called "highest", "higher", "high", "low" and "lowest". To assign traffic to these classes, we have combined sensible rules into mega-rules.

If you remove the standard rules, you will most likely disable or damage the traffic-shaping operation of cFosSpeed. So, it's an excellent idea to leave them in place and just add your own rules below these rules!

-highest
This feeds packets to the "highest" class.

All cFosSpeed pings are matched by this rule.
-higher
This feeds packets to the "higher" class.

All TCP ACKs are matched by this rule. ACK is an empty TCP packet with no RST or FIN set.
-high
This feeds packets to the "high" class.

This rule matched all (by default) prioritized traffic: ICMP, DNS and NTP (on UPD), FTP command, POP, IMAP, Telnet, SSH, HTTP, HTTPS (on TCP).
-low
This feeds packets to the "low" class.
Powered by cFos Personal Net Web Server
Practice random kindness and senseless acts of beauty