ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/gvpe/src/connection.C
(Generate patch)

Comparing gvpe/src/connection.C (file contents):
Revision 1.8 by pcg, Sun Apr 6 04:17:36 2003 UTC vs.
Revision 1.21 by pcg, Thu Oct 16 02:28:36 2003 UTC

35#include "slog.h" 35#include "slog.h"
36#include "device.h" 36#include "device.h"
37#include "vpn.h" 37#include "vpn.h"
38#include "connection.h" 38#include "connection.h"
39 39
40#include "netcompat.h"
41
40#if !HAVE_RAND_PSEUDO_BYTES 42#if !HAVE_RAND_PSEUDO_BYTES
41# define RAND_pseudo_bytes RAND_bytes 43# define RAND_pseudo_bytes RAND_bytes
42#endif 44#endif
43 45
44#define MAGIC "vped\xbd\xc6\xdb\x82" // 8 bytes of magic 46#define MAGIC "vped\xbd\xc6\xdb\x82" // 8 bytes of magic
147 } 149 }
148} 150}
149 151
150////////////////////////////////////////////////////////////////////////////// 152//////////////////////////////////////////////////////////////////////////////
151 153
152void pkt_queue::put (tap_packet *p) 154void pkt_queue::put (net_packet *p)
153{ 155{
154 if (queue[i]) 156 if (queue[i])
155 { 157 {
156 delete queue[i]; 158 delete queue[i];
157 j = (j + 1) % QUEUEDEPTH; 159 j = (j + 1) % QUEUEDEPTH;
160 queue[i] = p; 162 queue[i] = p;
161 163
162 i = (i + 1) % QUEUEDEPTH; 164 i = (i + 1) % QUEUEDEPTH;
163} 165}
164 166
165tap_packet *pkt_queue::get () 167net_packet *pkt_queue::get ()
166{ 168{
167 tap_packet *p = queue[j]; 169 net_packet *p = queue[j];
168 170
169 if (p) 171 if (p)
170 { 172 {
171 queue[j] = 0; 173 queue[j] = 0;
172 j = (j + 1) % QUEUEDEPTH; 174 j = (j + 1) % QUEUEDEPTH;
197// only do action once every x seconds per host whole allowing bursts. 199// only do action once every x seconds per host whole allowing bursts.
198// this implementation ("splay list" ;) is inefficient, 200// this implementation ("splay list" ;) is inefficient,
199// but low on resources. 201// but low on resources.
200struct net_rate_limiter : list<net_rateinfo> 202struct net_rate_limiter : list<net_rateinfo>
201{ 203{
202 static const double ALPHA = 1. - 1. / 90.; // allow bursts 204 static const double ALPHA = 1. - 1. / 600.; // allow bursts
203 static const double CUTOFF = 20.; // one event every CUTOFF seconds 205 static const double CUTOFF = 10.; // one event every CUTOFF seconds
204 static const double EXPIRE = CUTOFF * 30.; // expire entries after this time 206 static const double EXPIRE = CUTOFF * 30.; // expire entries after this time
207 static const double MAXDIF = CUTOFF * (1. / (1. - ALPHA)); // maximum diff /count value
205 208
206 bool can (const sockinfo &si) { return can((u32)si.host); } 209 bool can (const sockinfo &si) { return can((u32)si.host); }
207 bool can (u32 host); 210 bool can (u32 host);
208}; 211};
209 212
210net_rate_limiter auth_rate_limiter, reset_rate_limiter; 213net_rate_limiter auth_rate_limiter, reset_rate_limiter;
211 214
225 { 228 {
226 net_rateinfo ri; 229 net_rateinfo ri;
227 230
228 ri.host = host; 231 ri.host = host;
229 ri.pcnt = 1.; 232 ri.pcnt = 1.;
230 ri.diff = CUTOFF * (1. / (1. - ALPHA)); 233 ri.diff = MAXDIF;
231 ri.last = NOW; 234 ri.last = NOW;
232 235
233 push_front (ri); 236 push_front (ri);
234 237
235 return true; 238 return true;
242 ri.pcnt = ri.pcnt * ALPHA; 245 ri.pcnt = ri.pcnt * ALPHA;
243 ri.diff = ri.diff * ALPHA + (NOW - ri.last); 246 ri.diff = ri.diff * ALPHA + (NOW - ri.last);
244 247
245 ri.last = NOW; 248 ri.last = NOW;
246 249
250 double dif = ri.diff / ri.pcnt;
251
247 bool send = ri.diff / ri.pcnt > CUTOFF; 252 bool send = dif > CUTOFF;
248 253
254 if (dif > MAXDIF)
255 {
256 ri.pcnt = 1.;
257 ri.diff = MAXDIF;
258 }
249 if (send) 259 else if (send)
250 ri.pcnt++; 260 ri.pcnt++;
251 261
252 push_front (ri); 262 push_front (ri);
253 263
254 return send; 264 return send;
467 set_hdr (type, dst); 477 set_hdr (type, dst);
468} 478}
469 479
470bool config_packet::chk_config () const 480bool config_packet::chk_config () const
471{ 481{
472 return prot_major == PROTOCOL_MAJOR 482 if (prot_major != PROTOCOL_MAJOR)
473 && randsize == RAND_SIZE 483 slog (L_WARN, _("major version mismatch (%d <=> %d)"), prot_major, PROTOCOL_MAJOR);
474 && hmaclen == HMACLENGTH 484 else if (randsize != RAND_SIZE)
475 && flags == curflags () 485 slog (L_WARN, _("rand size mismatch (%d <=> %d)"), randsize, RAND_SIZE);
486 else if (hmaclen != HMACLENGTH)
487 slog (L_WARN, _("hmac length mismatch (%d <=> %d)"), hmaclen, HMACLENGTH);
488 else if (flags != curflags ())
489 slog (L_WARN, _("flag mismatch (%x <=> %x)"), flags, curflags ());
476 && challengelen == sizeof (rsachallenge) 490 else if (challengelen != sizeof (rsachallenge))
491 slog (L_WARN, _("challenge length mismatch (%d <=> %d)"), challengelen, sizeof (rsachallenge));
477 && cipher_nid == htonl (EVP_CIPHER_nid (CIPHER)) 492 else if (cipher_nid != htonl (EVP_CIPHER_nid (CIPHER)))
493 slog (L_WARN, _("cipher mismatch (%x <=> %x)"), ntohl (cipher_nid), EVP_CIPHER_nid (CIPHER));
478 && digest_nid == htonl (EVP_MD_type (RSA_HASH)) 494 else if (digest_nid != htonl (EVP_MD_type (RSA_HASH)))
495 slog (L_WARN, _("digest mismatch (%x <=> %x)"), ntohl (digest_nid), EVP_MD_type (RSA_HASH));
479 && hmac_nid == htonl (EVP_MD_type (DIGEST)); 496 else if (hmac_nid != htonl (EVP_MD_type (DIGEST)))
497 slog (L_WARN, _("hmac mismatch (%x <=> %x)"), ntohl (hmac_nid), EVP_MD_type (DIGEST));
498 else
499 return true;
500
501 return false;
480} 502}
481 503
482struct auth_req_packet : config_packet 504struct auth_req_packet : config_packet
483{ 505{
484 char magic[8]; 506 char magic[8];
544 len = sizeof (*this) - sizeof (net_packet); 566 len = sizeof (*this) - sizeof (net_packet);
545 } 567 }
546}; 568};
547 569
548///////////////////////////////////////////////////////////////////////////// 570/////////////////////////////////////////////////////////////////////////////
571
572void
573connection::connection_established ()
574{
575 if (ictx && octx)
576 {
577 connectmode = conf->connectmode;
578
579 rekey.start (NOW + ::conf.rekey);
580 keepalive.start (NOW + ::conf.keepalive);
581
582 // send queued packets
583 if (ictx && octx)
584 {
585 while (tap_packet *p = (tap_packet *)data_queue.get ())
586 {
587 send_data_packet (p);
588 delete p;
589 }
590
591 while (vpn_packet *p = (vpn_packet *)vpn_queue.get ())
592 {
593 send_vpn_packet (p, si, IPTOS_RELIABILITY);
594 delete p;
595 }
596 }
597 }
598 else
599 {
600 retry_cnt = 0;
601 establish_connection.start (NOW + 5);
602 keepalive.reset ();
603 rekey.reset ();
604 }
605}
549 606
550void 607void
551connection::reset_si () 608connection::reset_si ()
552{ 609{
553 protocol = best_protocol (THISNODE->protocols & conf->protocols); 610 protocol = best_protocol (THISNODE->protocols & conf->protocols);
580 637
581 return si; 638 return si;
582} 639}
583 640
584void 641void
642connection::send_vpn_packet (vpn_packet *pkt, const sockinfo &si, int tos)
643{
644 if (!vpn->send_vpn_packet (pkt, si, tos))
645 reset_connection ();
646}
647
648void
585connection::send_ping (const sockinfo &si, u8 pong) 649connection::send_ping (const sockinfo &si, u8 pong)
586{ 650{
587 ping_packet *pkt = new ping_packet; 651 ping_packet *pkt = new ping_packet;
588 652
589 pkt->setup (conf->id, pong ? ping_packet::PT_PONG : ping_packet::PT_PING); 653 pkt->setup (conf->id, pong ? ping_packet::PT_PONG : ping_packet::PT_PING);
590 vpn->send_vpn_packet (pkt, si, IPTOS_LOWDELAY); 654 send_vpn_packet (pkt, si, IPTOS_LOWDELAY);
591 655
592 delete pkt; 656 delete pkt;
593} 657}
594 658
595void 659void
598 if (reset_rate_limiter.can (si) && connectmode != conf_node::C_DISABLED) 662 if (reset_rate_limiter.can (si) && connectmode != conf_node::C_DISABLED)
599 { 663 {
600 config_packet *pkt = new config_packet; 664 config_packet *pkt = new config_packet;
601 665
602 pkt->setup (vpn_packet::PT_RESET, conf->id); 666 pkt->setup (vpn_packet::PT_RESET, conf->id);
603 vpn->send_vpn_packet (pkt, si, IPTOS_MINCOST); 667 send_vpn_packet (pkt, si, IPTOS_MINCOST);
604 668
605 delete pkt; 669 delete pkt;
606 } 670 }
607} 671}
608 672
620 conf->rsa_key, RSA_PKCS1_OAEP_PADDING)) 684 conf->rsa_key, RSA_PKCS1_OAEP_PADDING))
621 fatal ("RSA_public_encrypt error"); 685 fatal ("RSA_public_encrypt error");
622 686
623 slog (L_TRACE, ">>%d PT_AUTH_REQ [%s]", conf->id, (const char *)si); 687 slog (L_TRACE, ">>%d PT_AUTH_REQ [%s]", conf->id, (const char *)si);
624 688
625 vpn->send_vpn_packet (pkt, si, IPTOS_RELIABILITY | IPTOS_LOWDELAY); // rsa is very very costly 689 send_vpn_packet (pkt, si, IPTOS_RELIABILITY | IPTOS_LOWDELAY); // rsa is very very costly
626
627 690
628 delete pkt; 691 delete pkt;
629} 692}
630 693
631void 694void
639 702
640 pkt->hmac_set (octx); 703 pkt->hmac_set (octx);
641 704
642 slog (L_TRACE, ">>%d PT_AUTH_RES [%s]", conf->id, (const char *)si); 705 slog (L_TRACE, ">>%d PT_AUTH_RES [%s]", conf->id, (const char *)si);
643 706
644 vpn->send_vpn_packet (pkt, si, IPTOS_RELIABILITY); // rsa is very very costly 707 send_vpn_packet (pkt, si, IPTOS_RELIABILITY); // rsa is very very costly
645 708
646 delete pkt; 709 delete pkt;
647} 710}
648 711
649void 712void
653 conf->id, rid, (const char *)rsi); 716 conf->id, rid, (const char *)rsi);
654 717
655 connect_info_packet *r = new connect_info_packet (conf->id, rid, rsi, rprotocols); 718 connect_info_packet *r = new connect_info_packet (conf->id, rid, rsi, rprotocols);
656 719
657 r->hmac_set (octx); 720 r->hmac_set (octx);
658 vpn->send_vpn_packet (r, si); 721 send_vpn_packet (r, si);
659 722
660 delete r; 723 delete r;
661} 724}
662 725
663void 726void
677 w.at = NOW + retry_int; 740 w.at = NOW + retry_int;
678 741
679 reset_si (); 742 reset_si ();
680 743
681 if (si.prot && !si.host) 744 if (si.prot && !si.host)
682 vpn->connect_request (conf->id); 745 vpn->send_connect_request (conf->id);
683 else 746 else
684 { 747 {
685 const sockinfo &dsi = forward_si (si); 748 const sockinfo &dsi = forward_si (si);
686 749
687 if (dsi.valid () && auth_rate_limiter.can (dsi)) 750 if (dsi.valid () && auth_rate_limiter.can (dsi))
737 reset_connection (); 800 reset_connection ();
738 establish_connection (); 801 establish_connection ();
739} 802}
740 803
741void 804void
742connection::send_data_packet (tap_packet *pkt, bool broadcast) 805connection::send_data_packet (tap_packet *pkt)
743{ 806{
744 vpndata_packet *p = new vpndata_packet; 807 vpndata_packet *p = new vpndata_packet;
745 int tos = 0; 808 int tos = 0;
746 809
747 if (conf->inherit_tos 810 // I am not hilarious about peeking into packets, but so be it.
748 && (*pkt)[12] == 0x08 && (*pkt)[13] == 0x00 // IP 811 if (conf->inherit_tos && pkt->is_ipv4 ())
749 && ((*pkt)[14] & 0xf0) == 0x40) // IPv4
750 tos = (*pkt)[15] & IPTOS_TOS_MASK; 812 tos = (*pkt)[15] & IPTOS_TOS_MASK;
751 813
752 p->setup (this, broadcast ? 0 : conf->id, &((*pkt)[6 + 6]), pkt->len - 6 - 6, ++oseqno); // skip 2 macs 814 p->setup (this, conf->id, &((*pkt)[6 + 6]), pkt->len - 6 - 6, ++oseqno); // skip 2 macs
753 vpn->send_vpn_packet (p, si, tos); 815 send_vpn_packet (p, si, tos);
754 816
755 delete p; 817 delete p;
756 818
757 if (oseqno > MAX_SEQNO) 819 if (oseqno > MAX_SEQNO)
758 rekey (); 820 rekey ();
759} 821}
760 822
761void 823void
762connection::inject_data_packet (tap_packet *pkt, bool broadcast) 824connection::inject_data_packet (tap_packet *pkt, bool broadcast/*TODO DDD*/)
763{ 825{
764 if (ictx && octx) 826 if (ictx && octx)
765 send_data_packet (pkt, broadcast); 827 send_data_packet (pkt);
766 else 828 else
767 { 829 {
768 if (!broadcast)//DDDD 830 if (!broadcast)//DDDD
769 queue.put (new tap_packet (*pkt)); 831 data_queue.put (new tap_packet (*pkt));
770 832
771 establish_connection (); 833 establish_connection ();
772 } 834 }
773} 835}
774 836
775void connection::inject_vpn_packet (vpn_packet *pkt, int tos) 837void connection::inject_vpn_packet (vpn_packet *pkt, int tos)
776{ 838{
777 if (ictx && octx) 839 if (ictx && octx)
778 vpn->send_vpn_packet (pkt, si, tos); 840 send_vpn_packet (pkt, si, tos);
779 else 841 else
842 {
843 vpn_queue.put (new vpn_packet (*pkt));
844
780 establish_connection (); 845 establish_connection ();
846 }
781} 847}
782 848
783void 849void
784connection::recv_vpn_packet (vpn_packet *pkt, const sockinfo &rsi) 850connection::recv_vpn_packet (vpn_packet *pkt, const sockinfo &rsi)
785{ 851{
844 rsachallenge k; 910 rsachallenge k;
845 911
846 if (0 > RSA_private_decrypt (sizeof (p->encr), 912 if (0 > RSA_private_decrypt (sizeof (p->encr),
847 (unsigned char *)&p->encr, (unsigned char *)&k, 913 (unsigned char *)&p->encr, (unsigned char *)&k,
848 ::conf.rsa_key, RSA_PKCS1_OAEP_PADDING)) 914 ::conf.rsa_key, RSA_PKCS1_OAEP_PADDING))
849 slog (L_ERR, _("%s(%s): challenge illegal or corrupted"), 915 slog (L_ERR, _("%s(%s): challenge illegal or corrupted (%s). mismatched key or config file?"),
850 conf->nodename, (const char *)rsi); 916 conf->nodename, (const char *)rsi, ERR_error_string (ERR_get_error (), 0));
851 else 917 else
852 { 918 {
853 retry_cnt = 0;
854 establish_connection.start (NOW + 8); //? ;)
855 keepalive.reset ();
856 rekey.reset ();
857
858 delete ictx;
859 ictx = 0;
860
861 delete octx; 919 delete octx;
862 920
863 octx = new crypto_ctx (k, 1); 921 octx = new crypto_ctx (k, 1);
864 oseqno = ntohl (*(u32 *)&k[CHG_SEQNO]) & 0x7fffffff; 922 oseqno = ntohl (*(u32 *)&k[CHG_SEQNO]) & 0x7fffffff;
865 923
866 conf->protocols = p->protocols; 924 conf->protocols = p->protocols;
925
867 send_auth_response (rsi, p->id, k); 926 send_auth_response (rsi, p->id, k);
927
928 connection_established ();
868 929
869 break; 930 break;
870 } 931 }
871 } 932 }
933 else
934 slog (L_WARN, _("%s(%s): protocol mismatch"),
935 conf->nodename, (const char *)rsi);
872 936
873 send_reset (rsi); 937 send_reset (rsi);
874 } 938 }
875 939
876 break; 940 break;
889 PROTOCOL_MINOR, conf->nodename, p->prot_minor); 953 PROTOCOL_MINOR, conf->nodename, p->prot_minor);
890 954
891 rsachallenge chg; 955 rsachallenge chg;
892 956
893 if (!rsa_cache.find (p->id, chg)) 957 if (!rsa_cache.find (p->id, chg))
958 {
894 slog (L_ERR, _("%s(%s): unrequested auth response"), 959 slog (L_ERR, _("%s(%s): unrequested auth response ignored"),
895 conf->nodename, (const char *)rsi); 960 conf->nodename, (const char *)rsi);
961 break;
962 }
896 else 963 else
897 { 964 {
898 crypto_ctx *cctx = new crypto_ctx (chg, 0); 965 crypto_ctx *cctx = new crypto_ctx (chg, 0);
899 966
900 if (!p->hmac_chk (cctx)) 967 if (!p->hmac_chk (cctx))
968 {
901 slog (L_ERR, _("%s(%s): hmac authentication error on auth response, received invalid packet\n" 969 slog (L_ERR, _("%s(%s): hmac authentication error on auth response, received invalid packet\n"
902 "could be an attack, or just corruption or an synchronization error"), 970 "could be an attack, or just corruption or an synchronization error"),
903 conf->nodename, (const char *)rsi); 971 conf->nodename, (const char *)rsi);
972 break;
973 }
904 else 974 else
905 { 975 {
906 rsaresponse h; 976 rsaresponse h;
907 977
908 rsa_hash (p->id, chg, h); 978 rsa_hash (p->id, chg, h);
916 iseqno.reset (ntohl (*(u32 *)&chg[CHG_SEQNO]) & 0x7fffffff); // at least 2**31 sequence numbers are valid 986 iseqno.reset (ntohl (*(u32 *)&chg[CHG_SEQNO]) & 0x7fffffff); // at least 2**31 sequence numbers are valid
917 987
918 si = rsi; 988 si = rsi;
919 protocol = rsi.prot; 989 protocol = rsi.prot;
920 990
921 rekey.start (NOW + ::conf.rekey); 991 connection_established ();
922 keepalive.start (NOW + ::conf.keepalive);
923
924 // send queued packets
925 while (tap_packet *p = queue.get ())
926 {
927 send_data_packet (p);
928 delete p;
929 }
930
931 connectmode = conf->connectmode;
932 992
933 slog (L_INFO, _("%s(%s): connection established, protocol version %d.%d"), 993 slog (L_INFO, _("%s(%s): connection established, protocol version %d.%d"),
934 conf->nodename, (const char *)rsi, 994 conf->nodename, (const char *)rsi,
935 p->prot_major, p->prot_minor); 995 p->prot_major, p->prot_minor);
936 996
962 1022
963 if (ictx && octx) 1023 if (ictx && octx)
964 { 1024 {
965 vpndata_packet *p = (vpndata_packet *)pkt; 1025 vpndata_packet *p = (vpndata_packet *)pkt;
966 1026
967 if (rsi == si) 1027 if (!p->hmac_chk (ictx))
1028 slog (L_ERR, _("%s(%s): hmac authentication error, received invalid packet\n"
1029 "could be an attack, or just corruption or an synchronization error"),
1030 conf->nodename, (const char *)rsi);
1031 else
968 { 1032 {
969 if (!p->hmac_chk (ictx))
970 slog (L_ERR, _("%s(%s): hmac authentication error, received invalid packet\n"
971 "could be an attack, or just corruption or an synchronization error"),
972 conf->nodename, (const char *)rsi);
973 else 1033 u32 seqno;
1034 tap_packet *d = p->unpack (this, seqno);
1035
1036 if (iseqno.recv_ok (seqno))
974 { 1037 {
975 u32 seqno; 1038 vpn->tap->send (d);
976 tap_packet *d = p->unpack (this, seqno);
977 1039
978 if (iseqno.recv_ok (seqno)) 1040 if (si != rsi)
979 { 1041 {
980 vpn->tap->send (d); 1042 // fast re-sync on connection changes, useful especially for tcp/ip
981
982 if (p->dst () == 0) // re-broadcast
983 for (vpn::conns_vector::iterator i = vpn->conns.begin (); i != vpn->conns.end (); ++i)
984 {
985 connection *c = *i;
986
987 if (c->conf != THISNODE && c->conf != conf)
988 c->inject_data_packet (d);
989 }
990
991 delete d;
992
993 break; 1043 si = rsi;
1044
1045 slog (L_INFO, _("%s(%s): socket address changed to %s"),
1046 conf->nodename, (const char *)si, (const char *)rsi);
994 } 1047 }
1048
1049 delete d;
1050
1051 break;
995 } 1052 }
996 } 1053 }
997 else
998 slog (L_ERR, _("received data packet from unknown source %s"), (const char *)rsi);
999 } 1054 }
1000 1055
1001 send_reset (rsi); 1056 send_reset (rsi);
1002 break; 1057 break;
1003 1058
1018 // send connect_info packets to both sides, in case one is 1073 // send connect_info packets to both sides, in case one is
1019 // behind a nat firewall (or both ;) 1074 // behind a nat firewall (or both ;)
1020 c->send_connect_info (conf->id, si, conf->protocols); 1075 c->send_connect_info (conf->id, si, conf->protocols);
1021 send_connect_info (c->conf->id, c->si, c->conf->protocols); 1076 send_connect_info (c->conf->id, c->si, c->conf->protocols);
1022 } 1077 }
1078 else
1079 c->establish_connection ();
1023 } 1080 }
1024 1081
1025 break; 1082 break;
1026 1083
1027 case vpn_packet::PT_CONNECT_INFO: 1084 case vpn_packet::PT_CONNECT_INFO:
1067 || THISNODE->connectmode != conf_node::C_ONDEMAND) 1124 || THISNODE->connectmode != conf_node::C_ONDEMAND)
1068 { 1125 {
1069 send_ping (si); 1126 send_ping (si);
1070 w.at = NOW + 5; 1127 w.at = NOW + 5;
1071 } 1128 }
1129 else if (NOW < last_activity + ::conf.keepalive + 10)
1130 // hold ondemand connections implicitly a few seconds longer
1131 // should delete octx, though, or something like that ;)
1132 w.at = last_activity + ::conf.keepalive + 10;
1072 else 1133 else
1073 reset_connection (); 1134 reset_connection ();
1074} 1135}
1075 1136
1076void connection::connect_request (int id) 1137void connection::send_connect_request (int id)
1077{ 1138{
1078 connect_req_packet *p = new connect_req_packet (conf->id, id, conf->protocols); 1139 connect_req_packet *p = new connect_req_packet (conf->id, id, conf->protocols);
1079 1140
1080 slog (L_TRACE, ">>%d PT_CONNECT_REQ(%d)", conf->id, id); 1141 slog (L_TRACE, ">>%d PT_CONNECT_REQ(%d)", conf->id, id);
1081 p->hmac_set (octx); 1142 p->hmac_set (octx);
1082 vpn->send_vpn_packet (p, si); 1143 send_vpn_packet (p, si);
1083 1144
1084 delete p; 1145 delete p;
1085} 1146}
1086 1147
1087void connection::script_node () 1148void connection::script_node ()

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines